' > atest/results/index.html
- zip -r -j site.zip atest/results > no_output 2>&1
- curl -s -H "Content-Type: application/zip" -H "Authorization: Bearer ${{ secrets.NETLIFY_TOKEN }}" --data-binary "@site.zip" https://api.netlify.com/api/v1/sites > response.json
- echo "REPORT_URL=$(cat response.json|python -c "import sys, json; print('https://' + json.load(sys.stdin)['subdomain'] + '.netlify.com')")" >> $GITHUB_ENV
- echo "JOB_STATUS=$(python -c "print('${{ job.status }}'.lower())")" >> $GITHUB_ENV
- if: always() && job.status == 'failure' && runner.os != 'Windows'
-
- - name: Upload results on Windows
- run: |
- echo '' > atest/results/index.html
- zip -r -j site.zip atest/results > no_output 2>&1
- curl -s -H "Content-Type: application/zip" -H "Authorization: Bearer ${{ secrets.NETLIFY_TOKEN }}" --data-binary "@site.zip" https://api.netlify.com/api/v1/sites > response.json
- echo "REPORT_URL=$(cat response.json|python -c "import sys, json; print('https://' + json.load(sys.stdin)['subdomain'] + '.netlify.com')")" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- echo "JOB_STATUS=$(python -c "print('${{ job.status }}'.lower())")" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- if: always() && job.status == 'failure' && runner.os == 'Windows'
-
- - uses: octokit/request-action@89697eb6635e52c6e1e5559f15b5c91ba5100cb0
- name: Update status with Github Status API
- id: update_status
- with:
- route: POST /repos/:repository/statuses/:sha
- repository: ${{ github.repository }}
- sha: ${{ github.sha }}
- state: "${{env.JOB_STATUS}}"
- target_url: "${{env.REPORT_URL}}"
- description: "Link to test report."
- context: at-results-${{ matrix.python-version }}-${{ matrix.os }}
+ - name: Install and run rflogs
+ if: failure()
env:
- GITHUB_TOKEN: ${{ secrets.STATUS_UPLOAD_TOKEN }}
- if: always() && job.status == 'failure'
+ RFLOGS_API_KEY: ${{ secrets.RFLOGS_API_KEY }}
+ working-directory: atest/results
+ shell: python
+ run: |
+ import os
+ import glob
+ import subprocess
+
+ # Install rflogs
+ subprocess.check_call(["pip", "install", "rflogs"])
+
+ # Find the first directory containing log.html
+ log_files = glob.glob("**/log.html", recursive=True)
+ if log_files:
+ result_dir = os.path.dirname(log_files[0])
+ print(f"Result directory: {result_dir}")
+
+ # Construct the rflogs command
+ cmd = [
+ "rflogs", "upload",
+ "--tag", f"workflow:${{ github.workflow }}",
+ "--tag", f"os:${{ runner.os }}",
+ "--tag", f"python-version:${{ matrix.python-version }}",
+ "--tag", f"branch:${{ github.head_ref || github.ref_name }}",
+ result_dir
+ ]
+
+ # Run rflogs upload
+ subprocess.check_call(cmd)
+ else:
+ print("No directory containing log.html found")
+ exit(1)
diff --git a/.github/workflows/acceptance_tests_cpython_pr.yml b/.github/workflows/acceptance_tests_cpython_pr.yml
index a47b2483c8f..1b49dc448fe 100644
--- a/.github/workflows/acceptance_tests_cpython_pr.yml
+++ b/.github/workflows/acceptance_tests_cpython_pr.yml
@@ -15,7 +15,7 @@ jobs:
fail-fast: true
matrix:
os: [ 'ubuntu-latest', 'windows-latest' ]
- python-version: [ '3.8', '3.12' ]
+ python-version: [ '3.8', '3.13' ]
include:
- os: ubuntu-latest
set_display: export DISPLAY=:99; Xvfb :99 -screen 0 1024x768x24 -ac -noreset & sleep 3
@@ -29,9 +29,9 @@ jobs:
- uses: actions/checkout@v4
- name: Setup python for starting the tests
- uses: actions/setup-python@v5.0.0
+ uses: actions/setup-python@v5.6.0
with:
- python-version: '3.11'
+ python-version: '3.13'
architecture: 'x64'
- name: Get test starter Python at Windows
@@ -43,7 +43,7 @@ jobs:
if: runner.os != 'Windows'
- name: Setup python ${{ matrix.python-version }} for running the tests
- uses: actions/setup-python@v5.0.0
+ uses: actions/setup-python@v5.6.0
with:
python-version: ${{ matrix.python-version }}
architecture: 'x64'
@@ -56,16 +56,10 @@ jobs:
run: echo "BASE_PYTHON=$(which python)" >> $GITHUB_ENV
if: runner.os != 'Windows'
- - name: Install Report handling tools to Windows
- run: |
- choco install curl -y --no-progress
- choco install zip -y --no-progress
- if: runner.os == 'Windows'
-
- - name: Install screen and report handling tools, and other required libraries to Linux
+ - name: Install screen and other required libraries to Linux
run: |
sudo apt-get update
- sudo apt-get -y -q install xvfb scrot zip curl libxml2-dev libxslt1-dev
+ sudo apt-get -y -q install xvfb scrot libxml2-dev libxslt1-dev
if: contains(matrix.os, 'ubuntu')
- name: Run acceptance tests
@@ -76,16 +70,6 @@ jobs:
${{ matrix.set_display }}
${{ env.ATEST_PYTHON }} atest/run.py --interpreter ${{ env.BASE_PYTHON }} --exclude no-ci ${{ matrix.atest_args }} atest/robot
- - name: Delete output.xml (on Win)
- run: |
- Get-ChildItem atest/results -Include output.xml -Recurse | Remove-Item
- if: always() && runner.os == 'Windows'
-
- - name: Delete output.xml (on Unix-like)
- run: |
- find atest/results -type f -name 'output.xml' -exec rm {} +
- if: always() && runner.os != 'Windows'
-
- name: Archive acceptances test results
uses: actions/upload-artifact@v4
with:
@@ -93,35 +77,38 @@ jobs:
path: atest/results
if: always() && job.status == 'failure'
- - name: Upload results on *nix
- run: |
- echo '' > atest/results/index.html
- zip -r -j site.zip atest/results > no_output 2>&1
- curl -s -H "Content-Type: application/zip" -H "Authorization: Bearer ${{ secrets.NETLIFY_TOKEN }}" --data-binary "@site.zip" https://api.netlify.com/api/v1/sites > response.json
- echo "REPORT_URL=$(cat response.json|python -c "import sys, json; print('https://' + json.load(sys.stdin)['subdomain'] + '.netlify.com')")" >> $GITHUB_ENV
- echo "JOB_STATUS=$(python -c "print('${{ job.status }}'.lower())")" >> $GITHUB_ENV
- if: always() && job.status == 'failure' && runner.os != 'Windows'
-
- - name: Upload results on Windows
- run: |
- echo '' > atest/results/index.html
- zip -r -j site.zip atest/results > no_output 2>&1
- curl -s -H "Content-Type: application/zip" -H "Authorization: Bearer ${{ secrets.NETLIFY_TOKEN }}" --data-binary "@site.zip" https://api.netlify.com/api/v1/sites > response.json
- echo "REPORT_URL=$(cat response.json|python -c "import sys, json; print('https://' + json.load(sys.stdin)['subdomain'] + '.netlify.com')")" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- echo "JOB_STATUS=$(python -c "print('${{ job.status }}'.lower())")" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- if: always() && job.status == 'failure' && runner.os == 'Windows'
-
- - uses: octokit/request-action@89697eb6635e52c6e1e5559f15b5c91ba5100cb0
- name: Update status with Github Status API
- id: update_status
- with:
- route: POST /repos/:repository/statuses/:sha
- repository: ${{ github.repository }}
- sha: ${{ github.sha }}
- state: "${{env.JOB_STATUS}}"
- target_url: "${{env.REPORT_URL}}"
- description: "Link to test report."
- context: at-results-${{ matrix.python-version }}-${{ matrix.os }}
+ - name: Install and run rflogs
+ if: failure()
env:
- GITHUB_TOKEN: ${{ secrets.STATUS_UPLOAD_TOKEN }}
- if: always() && job.status == 'failure'
+ RFLOGS_API_KEY: ${{ secrets.RFLOGS_API_KEY }}
+ working-directory: atest/results
+ shell: python
+ run: |
+ import os
+ import glob
+ import subprocess
+
+ # Install rflogs
+ subprocess.check_call(["pip", "install", "rflogs"])
+
+ # Find the first directory containing log.html
+ log_files = glob.glob("**/log.html", recursive=True)
+ if log_files:
+ result_dir = os.path.dirname(log_files[0])
+ print(f"Result directory: {result_dir}")
+
+ # Construct the rflogs command
+ cmd = [
+ "rflogs", "upload",
+ "--tag", f"workflow:${{ github.workflow }}",
+ "--tag", f"os:${{ runner.os }}",
+ "--tag", f"python-version:${{ matrix.python-version }}",
+ "--tag", f"branch:${{ github.head_ref || github.ref_name }}",
+ result_dir
+ ]
+
+ # Run rflogs upload
+ subprocess.check_call(cmd)
+ else:
+ print("No directory containing log.html found")
+ exit(1)
diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml
index 53607b198ab..c52d155900d 100644
--- a/.github/workflows/unit_tests.yml
+++ b/.github/workflows/unit_tests.yml
@@ -5,6 +5,7 @@ on:
branches:
- main
- master
+ - v*-maintenance
paths:
- '.github/workflows/**'
- 'src/**'
@@ -19,7 +20,7 @@ jobs:
fail-fast: false
matrix:
os: [ 'ubuntu-latest', 'windows-latest' ]
- python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12', 'pypy-3.8' ]
+ python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', 'pypy-3.8' ]
exclude:
- os: windows-latest
python-version: 'pypy-3.8'
@@ -31,7 +32,7 @@ jobs:
- uses: actions/checkout@v4
- name: Setup python ${{ matrix.python-version }}
- uses: actions/setup-python@v5.0.0
+ uses: actions/setup-python@v5.6.0
with:
python-version: ${{ matrix.python-version }}
architecture: 'x64'
diff --git a/.github/workflows/unit_tests_pr.yml b/.github/workflows/unit_tests_pr.yml
index c8bc777c286..91eb380d330 100644
--- a/.github/workflows/unit_tests_pr.yml
+++ b/.github/workflows/unit_tests_pr.yml
@@ -24,7 +24,7 @@ jobs:
- uses: actions/checkout@v4
- name: Setup python ${{ matrix.python-version }}
- uses: actions/setup-python@v5.0.0
+ uses: actions/setup-python@v5.6.0
with:
python-version: ${{ matrix.python-version }}
architecture: 'x64'
diff --git a/.github/workflows/upload_test_reports.yml b/.github/workflows/upload_test_reports.yml
deleted file mode 100644
index e05de8214c4..00000000000
--- a/.github/workflows/upload_test_reports.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-name: Upload test results
-
-on: [status]
-
-jobs:
- upload_test_results:
- runs-on: ubuntu-latest
- name: Upload results from ${{ github.event.name }}
- steps:
- - run: echo ${{ github.event }}
diff --git a/.github/workflows/web_tests.yml b/.github/workflows/web_tests.yml
new file mode 100644
index 00000000000..8e7cc6f03c9
--- /dev/null
+++ b/.github/workflows/web_tests.yml
@@ -0,0 +1,30 @@
+name: Web tests with jest
+
+on:
+ push:
+ branches:
+ - main
+ - master
+ - v*-maintenance
+
+ paths:
+ - '.github/workflows/**'
+ - 'src/web**'
+ - '!**/*.rst'
+
+jobs:
+ jest_tests:
+
+ runs-on: 'ubuntu-latest'
+
+ name: Jest tests for the web components
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: "16"
+ - name: Run tests
+ working-directory: ./src/web
+ run: npm install && npm run test
diff --git a/.gitignore b/.gitignore
index 4028dcaab8f..880555b6da1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,3 +30,6 @@ __pycache__
.settings
.jython_cache
.mypy_cache/
+node_modules
+.cache/
+.parcel-cache/
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
index dffec091319..bd5c3733053 100644
--- a/.readthedocs.yaml
+++ b/.readthedocs.yaml
@@ -20,6 +20,6 @@ sphinx:
# - pdf
# Optionally declare the Python requirements required to build your docs
-#python:
-# install:
-# - requirements: docs/requirements.txt
+python:
+ install:
+ - requirements: doc/api/requirements.txt
diff --git a/BUILD.rst b/BUILD.rst
index 1c0d907b4ba..c098c49c4b7 100644
--- a/BUILD.rst
+++ b/BUILD.rst
@@ -76,7 +76,7 @@ __ https://github.com/robotframework/robotframework/tree/master/atest#schema-val
Preparation
-----------
-1. Check that you are on the master branch and have nothing left to commit,
+1. Check that you are on the right branch and have nothing left to commit,
pull, or push::
git branch
@@ -84,22 +84,27 @@ Preparation
git pull --rebase
git push
-2. Clean up::
+2. Make sure code is formatted properly::
+
+ invoke format
+ git status
+
+3. Clean up::
invoke clean
-3. Set version information to a shell variable to ease copy-pasting further
+4. Set version information to a shell variable to ease copy-pasting further
commands. Add ``aN``, ``bN`` or ``rcN`` postfix if creating a pre-release::
VERSION=
- For example, ``VERSION=3.0.1`` or ``VERSION=3.1a2``.
+ For example, ``VERSION=7.1.1`` or ``VERSION=7.2a2``. No ``v`` prefix!
Release notes
-------------
-1. Create personal `GitHub access token`__ to be able to access issue tracker
- programmatically. The token needs only the `repo/public_repo` scope.
+1. Create a personal `GitHub access token`__ to be able to access issue tracker
+ programmatically. The token needs only the ``repo/public_repo`` scope.
2. Set GitHub user information into shell variables to ease running the
``invoke release-notes`` command in the next step::
@@ -119,7 +124,7 @@ Release notes
`__. Omit the ``-w`` option if you just want to get release
notes printed to the console, not written to a file.
- When generating release notes for a preview release like ``3.0.2rc1``,
+ When generating release notes for a preview release like ``7.2rc1``,
the list of issues is only going to contain issues with that label
(e.g. ``rc1``) or with a label of an earlier preview release (e.g.
``alpha1``, ``beta2``).
@@ -139,7 +144,7 @@ Release notes
issue tracker than in the generated release notes. This allows re-generating
the list of issues later if more issues are added.
-6. Add, commit and push::
+6. Commit and push changes::
git add doc/releasenotes/rf-$VERSION.rst
git commit -m "Release notes for $VERSION" doc/releasenotes/rf-$VERSION.rst
@@ -151,6 +156,22 @@ Release notes
__ https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token
+Update Libdoc templates
+-----------------------
+
+1. Prerequisites are listed in ``_. This step can be skipped
+ if there are no changes to Libdoc.
+
+2. Regenerate HTML template and update the list of supported localizations in
+ the ``--help`` text::
+
+ invoke build-libdoc
+
+3. Commit and push changes::
+
+ git commit -m "Update Libdoc templates" src/robot/htmldata/libdoc/libdoc.html src/robot/libdocpkg/languages.py
+ git push
+
Set version
-----------
@@ -189,27 +210,26 @@ Creating distributions
invoke clean
-3. Create and validate source distribution in zip format and
- `wheel `_::
+4. Create and validate source distribution and `wheel `_::
- python setup.py sdist --formats zip bdist_wheel
+ python setup.py sdist bdist_wheel
ls -l dist
twine check dist/*
Distributions can be tested locally if needed.
-4. Upload distributions to PyPI::
+5. Upload distributions to PyPI::
twine upload dist/*
-5. Verify that project pages at `PyPI
+6. Verify that project pages at `PyPI
`_ look good.
-6. Test installation::
+7. Test installation::
pip install --pre --upgrade robotframework
-7. Documentation
+8. Documentation
- For a reproducible build, set the ``SOURCE_DATE_EPOCH``
environment variable to a constant value, corresponding to the
@@ -229,14 +249,14 @@ Creating distributions
git checkout gh-pages
invoke add-docs $VERSION --push
- git checkout master
+ git checkout master # replace master with v*-maintenance if needed!
Post actions
------------
1. Back to master if needed::
- git checkout master
+ git checkout master # replace master with v*-maintenance if needed!
2. Set dev version based on the previous version::
@@ -256,28 +276,12 @@ Post actions
Announcements
-------------
-1. `robotframework-users `_
- and
- `robotframework-announce `_
- lists. The latter is not needed with preview releases but should be used
- at least with major updates. Notice that sending to it requires admin rights.
-
-2. Twitter. Either Tweet something yourself and make sure it's re-tweeted
- by `@robotframework `_, or send the
- message directly as `@robotframework`. This makes the note appear also
- at http://robotframework.org.
-
- Should include a link to more information. Possibly a link to the full
- release notes or an email to the aforementioned mailing lists.
-
-3. ``#devel`` and ``#general`` channels on Slack.
+1. ``#announcements`` channel on `Slack `_.
+ Use ``@channel`` at least with major releases.
-4. `Robot Framework LinkedIn
- `_ group.
+2. `Forum `_.
-5. Consider sending announcements, at least with major releases, also to other
- forums where we want to make the framework more well known. For example:
+3. `LinkedIn group `_. A personal
+ LinkedIn post is a good idea at least with bigger releases.
- - http://opensourcetesting.org
- - http://tech.groups.yahoo.com/group/agile-testing
- - http://lists.idyll.org/listinfo/testing-in-python
+4. `robotframework-users `_
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 59002154d82..37ff1c91fcc 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -8,14 +8,16 @@ There are also many other projects in the larger `Robot Framework ecosystem
`_ that you can contribute to. If you notice
a library or tool missing, there is hardly any better way to contribute
than creating your own project. Other great ways to contribute include
-answering questions and participating discussion on `robotframework-users
-`_ mailing list
-and other forums, as well as spreading the word about the framework one way or
-the other.
+answering questions and participating discussion on our
+`Slack `_,
+`Forum `_,
+`LinkedIn group `_,
+or other such discussion forum, speaking at conferences or local events,
+and spreading the word about the framework otherwise.
These guidelines expect readers to have a basic knowledge about open source
-as well as why and how to contribute to open source projects. If you are
-totally new to these topics, it may be a good idea to look at the generic
+as well as why and how to contribute to an open source project. If you are
+new to these topics, it may be a good idea to look at the generic
`Open Source Guides `_ first.
.. contents::
@@ -26,23 +28,21 @@ Submitting issues
-----------------
Bugs and enhancements are tracked in the `issue tracker
-`_. If you are
-unsure if something is a bug or is a feature worth implementing, you can
-first ask on `robotframework-users`_ mailing list, on `IRC
-`_
-(#robotframework on irc.freenode.net), or on `Slack
-`_. These and other similar
-forums, not the issue tracker, are also places where to ask general questions.
-
-Before submitting a new issue, it is always a good idea to check is the
-same bug or enhancement already reported. If it is, please add your comments
+`_. If you are unsure
+if something is a bug or is a feature worth implementing, you can
+first ask on the ``#devel`` channel on our Slack_. Slack and other such forums,
+not the issue tracker, are also places where to ask general questions about
+the framework.
+
+Before submitting a new issue, it is always a good idea to check if the
+same bug or enhancement is already reported. If it is, please add your comments
to the existing issue instead of creating a new one.
Reporting bugs
~~~~~~~~~~~~~~
-Explain the bug you have encountered so that others can understand it
-and preferably also reproduce it. Key things to have in good bug report:
+Explain the bug you have encountered so that others can understand it and
+preferably also reproduce it. Key things to include in good bug report:
1. Version information
@@ -50,6 +50,8 @@ and preferably also reproduce it. Key things to have in good bug report:
- Python interpreter version
- Operating system and its version
+ Typically including the output of ``robot --version`` is enough.
+
2. Steps to reproduce the problem. With more complex problems it is often
a good idea to create a `short, self contained, correct example (SSCCE)
`_.
@@ -64,9 +66,11 @@ Enhancement requests
Describe the new feature and use cases for it in as much detail as possible.
Especially with larger enhancements, be prepared to contribute the code
-in the form of a pull request as explained below or to pay someone for the work.
-Consider also would it be better to implement this functionality as a separate
-tool outside the core framework.
+in the form of a pull request as explained below. If you would like to sponsor
+a development of a certain feature, you can contact the `Robot Framework
+Foundation `_.
+Consider also would it be better to implement new functionality as a separate
+library or tool outside the core framework.
Code contributions
------------------
@@ -117,53 +121,283 @@ create dedicated topic branches for pull requests instead of creating
them based on the master branch. This is especially important if you plan to
work on multiple pull requests at the same time.
+Development dependencies
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Code formatting and other tasks require external tools to be installed. All
+of them are listed in the ``_ file and you can install
+them by running::
+
+ pip install -r requirements-dev.txt
+
Coding conventions
~~~~~~~~~~~~~~~~~~
-General guidelines
-''''''''''''''''''
+Robot Framework follows the general Python code conventions defined in `PEP-8
+`_. Code is `automatically formatted`__, but
+`manual adjustments`__ may sometimes be needed.
-Robot Framework uses the general Python code conventions defined in `PEP-8
-`_. In addition to that, we try
-to write `idiomatic Python
-`_
-and follow the `SOLID principles
-`_ with all
-new code. An important guideline is that the code should be clear enough that
-comments are generally not needed.
+__ `Automatic formatting`_
+__ `Manual formatting adjustments`_
-All code, including test code, must be compatible with all supported Python
-interpreters and versions. Most importantly this means that the code must
-support both Python 2 and Python 3.
+Automatic formatting
+''''''''''''''''''''
-Line length
-'''''''''''
+The code is automatically linted and formatted using a combination of tools
+that are driven by an `Invoke `_ task::
-Maximum line length with Python code, including docstrings and comments, is 88
-characters. This is also what `Black `__ uses
-by default and `their documentation
-`__
-explains why. Notice that we do not have immediate plans to actually take Black
-into use but we may consider that later.
+ invoke format
-With Robot Framework tests the maximum line length is 100.
+Make sure to run this command before creating a pull request!
-Whitespace
-''''''''''
+By default the task formats Python code under ``src``, ``atest`` and ``utest``
+directories, but it can be configured to format only certain directories
+or files::
+
+ invoke format -t src
-We are pretty picky about using whitespace. We follow `PEP-8`_ in how to use
-blank lines and whitespace in general, but we also have some stricter rules:
+Formatting is done in multiple phases:
-- No blank lines inside functions.
-- No blank lines between a class declaration and class attributes or between
- attributes.
-- Indentation using spaces, not tabs.
-- No trailing spaces.
-- No extra empty lines at the end of the file.
-- Files must end with a newline.
+ 1. Code is linted using `Ruff `_ . If linting
+ fails, the formatting process is stopped.
+ 2. Code is formatted code using `Black `_.
+ We plan to switch to Ruff as soon as they stop removing the
+ `empty row after the class declaration`__.
+ 3. Multiline imports are reformatted using `isort `_.
+ We use the "`hanging grid grouped`__" style to use less vertical space compared
+ to having each imported item on its own row. Public APIs using `redundant import
+ aliases`__ are not reformatted, though.
-Most of these rules are such that any decent text editor or IDE can be
-configured to automatically format files according to them.
+Tool configurations are in the ``_ file.
+
+__ https://github.com/astral-sh/ruff/issues/9745
+__ https://pycqa.github.io/isort/docs/configuration/multi_line_output_modes.html#5-hanging-grid-grouped
+__ https://typing.python.org/en/latest/spec/distributing.html#import-conventions
+
+Manual formatting adjustments
+'''''''''''''''''''''''''''''
+
+Automatic formatting works pretty well, but there are some cases where the results
+are suboptimal and manual adjustments are needed.
+
+.. note:: As a contributor, you do not need to care about this if you do not want to.
+ Maintainers can fix these issues themselves after merging your pull request.
+ Just running the aforementioned ``invoke format`` is enough.
+
+Force lists to have one item per row
+````````````````````````````````````
+
+Automatic formatting has three modes how to handle lists:
+
+- Short lists are formatted on a single row. This includes list items and opening
+ and closing braces and other markers.
+- If all list items fit into a single row, but the whole list with opening and
+ closing markers does not, items are placed into a single row and opening and
+ closing markers are on their own rows.
+- Long lists are formatted so that all list items are own their own rows and
+ opening and closing markers are on their own rows as well.
+
+In addition to lists and other containers, the above applies also to function
+calls and function signatures:
+
+.. sourcecode:: python
+
+ def short(first_arg: Iterable[int], second_arg: int = 0) -> int:
+ ...
+
+ def medium(
+ first_arg: Iterable[float], second_arg: float = 0.0, third_arg: bool = True
+ ) -> int:
+ ...
+
+ def long(
+ first_arg: Iterable[float],
+ second_arg: float = 0.0,
+ third_arg: bool = True,
+ fourth_arg: bool = False,
+ ) -> int:
+ ...
+
+This formatting is typically fine, but similar code being formatted differently
+in a single file can look inconsistent. Having multiple items in a single row, as in
+the ``medium`` example above, can also make the code hard to read. A simple fix
+is forcing list items to own rows by adding a `magic trailing comma`__ and running
+auto-formatter again:
+
+.. sourcecode:: python
+
+ def short(first_arg: Iterable[int], second_arg: int = 0) -> int:
+ ...
+
+ def medium(
+ first_arg: Iterable[float],
+ second_arg: float = 0.0,
+ third_arg: bool = True,
+ ) -> int:
+ ...
+
+ def long(
+ first_arg: Iterable[float],
+ second_arg: float = 0.0,
+ third_arg: bool = True,
+ fourth_arg: bool = False,
+ ) -> int:
+ ...
+
+Lists and signatures fitting into a single line, such as the ``short`` example above,
+should typically not be forced to multiple lines.
+
+__ https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#the-magic-trailing-comma
+
+Force multi-line lists to have multiple items per row
+`````````````````````````````````````````````````````
+
+Automatically formatting all list items into own rows uses a lot of vertical space.
+This is typically not a problem, but with long lists having simple items it can
+be somewhat annoying:
+
+.. sourcecode:: python
+
+ class Branches(
+ BaseBranches[
+ 'Keyword',
+ 'For',
+ 'While',
+ 'Group',
+ 'If',
+ 'Try',
+ 'Var',
+ 'Return',
+ 'Continue',
+ 'Break',
+ 'Message',
+ 'Error',
+ IT,
+ ]
+ ):
+ __slots__ = ()
+
+
+ added_in_rf60 = {
+ "bg",
+ "bs",
+ "cs",
+ "de",
+ "en",
+ "es",
+ "fi",
+ "fr",
+ "hi",
+ "it",
+ "nl",
+ "pl",
+ "pt",
+ "pt-BR",
+ "ro",
+ "ru",
+ "sv",
+ "th",
+ "tr",
+ "uk",
+ "zh-CN",
+ "zh-TW",
+ }
+
+The best way to fix this is disabling formatting altogether with the ``# fmt: skip``
+pragma. The code should be formatted so that opening and closing list markers
+are on their own rows, list items are wrapped, and the ``# fmt: skip`` pragma
+is placed after the closing list marker:
+
+.. sourcecode:: python
+
+ class Branches(BaseBranches[
+ "Keyword", "For", "While", "Group", "If", "Try", "Var", "Return", "Continue",
+ "Break", "Message", "Error", IT,
+ ]): # fmt: skip
+ __slots__ = ()
+
+
+ added_in_rf60 = {
+ "bg", "bs", "cs", "de", "en", "es", "fi", "fr", "hi", "it", "nl", "pl",
+ "pt", "pt-BR", "ro", "ru", "sv", "th", "tr", "uk", "zh-CN", "zh-TW",
+ } # fmt: skip
+
+Handle Boolean expressions
+``````````````````````````
+
+Autoformatting handles Boolean expressions having two items that do not fit into
+a single line *really* strangely:
+
+.. sourcecode:: python
+
+ ext = getattr(self.parser, 'EXTENSION', None) or getattr(
+ self.parser, 'extension', None
+ )
+
+ return self._get_runner_from_resource_files(
+ name
+ ) or self._get_runner_from_libraries(name)
+
+Expressions having three or more items would be grouped with parentheses and
+`there is an issue`__ about doing that also if there are two items. A workaround
+is using parentheses and disabling formatting with the ``# fmt: skip`` pragma:
+
+.. sourcecode:: python
+
+ ext = (
+ getattr(self.parser, 'EXTENSION', None)
+ or getattr(self.parser, 'extension', None)
+ ) # fmt: skip
+
+ return (
+ self._get_runner_from_resource_files(name)
+ or self._get_runner_from_libraries(name)
+ ) # fmt: skip
+
+__ https://github.com/psf/black/issues/2156
+
+Inline comment handling
+```````````````````````
+
+Autoformatting normalizes the number of spaces before an inline comment into two.
+That is typically fine, but if subsequent lines use inline comments, the result
+can be suboptimal__:
+
+.. sourcecode:: python
+
+ TypeHint = Union[
+ type, # Actual type.
+ str, # Type name or alias.
+ UnionType, # Union syntax (e.g. `int | float`).
+ 'tuple[TypeHint, ...]', # Tuple of type hints. Behaves like a union.
+ ]
+
+A solution is manually aligning comments and disabling autoformatting:
+
+.. sourcecode:: python
+
+ TypeHint = Union[
+ type, # Actual type.
+ str, # Type name or alias.
+ UnionType, # Union syntax (e.g. `int | float`).
+ "tuple[TypeHint, ...]" # Tuple of type hints. Behaves like a union.
+ ] # fmt: skip
+
+In the above example formatting is disabled with the ``# fmt: skip`` pragma, but
+it does not work if inline comments are not related to a single statement. In such
+cases the ``# fmt: off`` and ``# fmt: on`` pair can be used instead. In this example
+formatting is disabled to allow aligning constant values in addition to comments:
+
+.. sourcecode:: python
+
+ # fmt: off
+ INFO_PRINTED = 251 # --help or --version
+ DATA_ERROR = 252 # Invalid data or cli args
+ STOPPED_BY_USER = 253 # KeyboardInterrupt or SystemExit
+ FRAMEWORK_ERROR = 255 # Unexpected error
+ # fmt: on
+
+__ https://github.com/psf/black/issues/4651
Docstrings
''''''''''
@@ -174,6 +408,27 @@ internal code. When docstrings are added, they should follow `PEP-257
section below for more details about documentation syntax, generating
API docs, etc.
+Type hints
+''''''''''
+
+All public APIs must have type hints and adding type hints also to new internal
+code is recommended. Full type coverage is not a goal at the moment, though.
+
+Type hints should follow the Python `Typing Best Practices
+`_ with the
+following exceptions:
+
+- Annotation features are restricted to the minimum Python version supported by
+ Robot Framework.
+- Annotations should use the stringified format for annotations not supported
+ by the minimum supported Python version. For example, ``"int | float"``
+ instead of ``Union[int, float]`` and ``"list[int]"`` instead of ``List[int]``.
+ Type aliases are an exception to this rule.
+- Keywords accepting either an integer or a float should typically be annotated as
+ ``int | float`` instead of just ``float``. This way argument conversion tries to
+ first convert arguments to an integer and only converts to a float if that fails.
+- No ``-> None`` annotation on functions that do not explicitly return anything.
+
Documentation
~~~~~~~~~~~~~
@@ -186,7 +441,7 @@ User Guide
Robot Framework's features are explained in the `User Guide
`_. It is generated
using a custom script based on the source in `reStructuredText
-`_ format. For more details about
+`_ format. For more details about
editing and generating it see ``_.
Libraries
@@ -201,6 +456,10 @@ tool. Documentation must use Robot Framework's own `documentation formatting
`_
and follow these guidelines:
+- All new enhancements or changes should have a note telling when the change
+ was introduced. Often adding something like ``New in Robot Framework 7.3.``
+ is enough.
+
- Other keywords and sections in the library introduction can be referenced
with internal links created with backticks like ```Example Keyword```.
@@ -210,12 +469,7 @@ and follow these guidelines:
- Examples are recommended whenever the new keyword or enhanced functionality is
not trivial.
-- All new enhancements or changes should have a note telling when the change
- was introduced. Often adding something like ``New in Robot Framework 3.1.``
- is enough.
-
-Library documentation can be generated using `Invoke `_
-by running command
+Library documentation can be generated using Invoke_ by running command
::
@@ -227,8 +481,7 @@ where ```` is the name of the library or its unique prefix. Run
invoke --help library-docs
-for more information see ``_ for details about installing and
-using Invoke.
+for more information.
API documentation
'''''''''''''''''
@@ -245,11 +498,6 @@ Documentation can be created locally using ``_ script
that unfortunately creates a lot of errors on the console. Releases API docs
are visible at https://robot-framework.readthedocs.org/.
-Robot Framework's public API docs are lacking in many ways. All public
-classes are not yet documented, existing documentation is somewhat scarce,
-and there could be more examples. Documentation improvements are highly
-appreciated!
-
Tests
~~~~~
@@ -262,24 +510,18 @@ or both.
Make sure to run all of the tests before submitting a pull request to be sure
that your changes do not break anything. If you can, test in multiple
environments and interpreters (Windows, Linux, OS X, different Python
-versions etc). Pull requests are also automatically tested on
-continuous integration.
+versions etc). Pull requests are also automatically tested by GitHub Actions.
Executing changed code
''''''''''''''''''''''
If you want to manually verify the changes, an easy approach is directly
running the ``_ script that is part of Robot Framework
-itself. Alternatively you can use the ``_ script that sets
+itself. Alternatively, you can use the ``_ script that sets
some command line options and environment variables to ease executing tests
under the ``_ directory. It also automatically creates a
``tmp`` directory in the project root and writes all outputs there.
-If you want to install the current code locally, you can do it like
-``python setup.py install`` as explained in ``_. For
-instructions how to create a distribution that allows installing elsewhere
-see ``_.
-
Acceptance tests
''''''''''''''''
diff --git a/INSTALL.rst b/INSTALL.rst
index 26a49927700..84f997343e7 100644
--- a/INSTALL.rst
+++ b/INSTALL.rst
@@ -47,7 +47,7 @@ Python version than the one provided by your distribution by default.
To check what Python version you have installed, you can run `python --version`
command in a terminal:
-.. sourcecode:: bash
+.. code:: bash
$ python --version
Python 3.10.13
@@ -58,7 +58,7 @@ specific command like `python3.8`. You need to use these version specific varian
also if you have multiple Python 3 versions installed and need to pinpoint which
one to use:
-.. sourcecode:: bash
+.. code:: bash
$ python3.11 --version
Python 3.11.7
@@ -89,7 +89,7 @@ to select the `Add Python 3.x to PATH` checkbox on the first dialog.
To make sure Python installation has been successful and Python has been
added to `PATH`, you can open the command prompt and execute `python --version`:
-.. sourcecode:: batch
+.. code:: batch
C:\>python --version
Python 3.10.9
@@ -98,7 +98,7 @@ If you install multiple Python versions on Windows, the version that is used
when you execute `python` is the one first in `PATH`. If you need to use others,
the easiest way is using the `py launcher`__:
-.. sourcecode:: batch
+.. code:: batch
C:\>py --version
Python 3.10.9
@@ -200,7 +200,7 @@ To make sure you have pip available, you can run `pip --version` or equivalent.
Examples on Linux:
-.. sourcecode:: bash
+.. code:: bash
$ pip --version
pip 23.2.1 from ... (python 3.10)
@@ -209,7 +209,7 @@ Examples on Linux:
Examples on Windows:
-.. sourcecode:: batch
+.. code:: batch
C:\> pip --version
pip 23.2.1 from ... (python 3.10)
@@ -229,7 +229,7 @@ shown below and pip_ documentation has more information and examples.
__ PyPI_
-.. sourcecode:: bash
+.. code:: bash
# Install the latest version (does not upgrade)
pip install robotframework
@@ -259,13 +259,13 @@ Another installation alternative is getting Robot Framework source code
and installing it using the provided `setup.py` script. This approach is
recommended only if you do not have pip_ available for some reason.
-You can get the source code by downloading a source distribution as a zip
-package from PyPI_ and extracting it. An alternative is cloning the GitHub_
+You can get the source code by downloading a source distribution package
+from PyPI_ and extracting it. An alternative is cloning the GitHub_
repository and checking out the needed release tag.
Once you have the source code, you can install it with the following command:
-.. sourcecode:: bash
+.. code:: bash
python setup.py install
@@ -280,7 +280,7 @@ Verifying installation
To make sure that the correct Robot Framework version has been installed, run
the following command:
-.. sourcecode:: bash
+.. code:: bash
$ robot --version
Robot Framework 7.0 (Python 3.10.3 on linux)
@@ -294,7 +294,7 @@ running `robot` will execute the one first in PATH_. To select explicitly,
you can run `python -m robot` and substitute `python` with the right Python
version.
-.. sourcecode:: bash
+.. code:: bash
$ python3.12 -m robot --version
Robot Framework 7.0 (Python 3.12.1 on linux)
diff --git a/atest/README.rst b/atest/README.rst
index 5856d5661b2..410ef5cdce8 100644
--- a/atest/README.rst
+++ b/atest/README.rst
@@ -137,7 +137,7 @@ require-yaml, require-lxml, require-screenshot
Require specified Python module or some other external tool to be installed.
Exclude like `--exclude require-lxml` if dependencies_ are not met.
-require-windows, require-py3.8, ...
+require-windows, require-py3.13, ...
Tests that require certain operating system or Python interpreter.
Excluded automatically outside these platforms.
diff --git a/atest/genrunner.py b/atest/genrunner.py
index c3c94af355d..89d331dd1b7 100755
--- a/atest/genrunner.py
+++ b/atest/genrunner.py
@@ -5,20 +5,20 @@
Usage: {tool} testdata/path/data.robot [robot/path/runner.robot]
"""
-from os.path import abspath, basename, dirname, exists, join
import os
-import sys
import re
+import sys
+from os.path import abspath, basename, dirname, exists, join
-if len(sys.argv) not in [2, 3] or not all(a.endswith('.robot') for a in sys.argv[1:]):
+if len(sys.argv) not in [2, 3] or not all(a.endswith(".robot") for a in sys.argv[1:]):
sys.exit(__doc__.format(tool=basename(sys.argv[0])))
-SEPARATOR = re.compile(r'\s{2,}|\t')
+SEPARATOR = re.compile(r"\s{2,}|\t")
INPATH = abspath(sys.argv[1])
-if join('atest', 'testdata') not in INPATH:
+if join("atest", "testdata") not in INPATH:
sys.exit("Input not under 'atest/testdata'.")
if len(sys.argv) == 2:
- OUTPATH = INPATH.replace(join('atest', 'testdata'), join('atest', 'robot'))
+ OUTPATH = INPATH.replace(join("atest", "testdata"), join("atest", "robot"))
else:
OUTPATH = sys.argv[2]
@@ -42,39 +42,45 @@ def __init__(self, name, tags=None):
line = line.rstrip()
if not line:
continue
- elif line.startswith('*'):
- name = SEPARATOR.split(line)[0].replace('*', '').replace(' ', '').upper()
- parsing_tests = name in ('TESTCASE', 'TESTCASES', 'TASK', 'TASKS')
- parsing_settings = name in ('SETTING', 'SETTINGS')
- elif parsing_tests and not SEPARATOR.match(line) and line[0] != '#':
- TESTS.append(TestCase(line.split(' ')[0]))
- elif parsing_tests and line.strip().startswith('[Tags]'):
- TESTS[-1].tags = line.split('[Tags]', 1)[1].split()
- elif parsing_settings and line.startswith(('Force Tags', 'Default Tags', 'Test Tags')):
- name, value = line.split(' ', 1)
- SETTINGS.append((name, value.strip()))
-
-
-with open(OUTPATH, 'w') as output:
- path = INPATH.split(join('atest', 'testdata'))[1][1:].replace(os.sep, '/')
- output.write('''\
+ elif line.startswith("*"):
+ name = SEPARATOR.split(line)[0].replace("*", "").replace(" ", "").upper()
+ parsing_tests = name in ("TESTCASES", "TASKS")
+ parsing_settings = name == "SETTINGS"
+ elif parsing_tests and not SEPARATOR.match(line) and line[0] != "#":
+ TESTS.append(TestCase(SEPARATOR.split(line)[0]))
+ elif parsing_tests and line.strip().startswith("[Tags]"):
+ TESTS[-1].tags = line.split("[Tags]", 1)[1].split()
+ elif parsing_settings and line.startswith("Test Tags"):
+ name, *values = SEPARATOR.split(line)
+ SETTINGS.append((name, values))
+
+
+with open(OUTPATH, "w") as output:
+ path = INPATH.split(join("atest", "testdata"))[1][1:].replace(os.sep, "/")
+ output.write(
+ f"""\
*** Settings ***
-Suite Setup Run Tests ${EMPTY} %s
-''' % path)
- for name, value in SETTINGS:
- output.write('%s%s\n' % (name.ljust(18), value))
- output.write('''\
+Suite Setup Run Tests ${{EMPTY}} {path}
+"""
+ )
+ for name, values in SETTINGS:
+ values = " ".join(values)
+ output.write(f"{name:18}{values}\n")
+ output.write(
+ """\
Resource atest_resource.robot
*** Test Cases ***
-''')
+"""
+ )
for test in TESTS:
- output.write(test.name + '\n')
+ output.write(test.name + "\n")
if test.tags:
- output.write(' [Tags] %s\n' % ' '.join(test.tags))
- output.write(' Check Test Case ${TESTNAME}\n')
+ tags = " ".join(test.tags)
+ output.write(f" [Tags] {tags}\n")
+ output.write(" Check Test Case ${TESTNAME}\n")
if test is not TESTS[-1]:
- output.write('\n')
+ output.write("\n")
print(OUTPATH)
diff --git a/atest/interpreter.py b/atest/interpreter.py
index 7723d043f0d..26a47fc1b09 100644
--- a/atest/interpreter.py
+++ b/atest/interpreter.py
@@ -1,15 +1,12 @@
import os
-from pathlib import Path
import re
import subprocess
import sys
-
-
-ROBOT_DIR = Path(__file__).parent.parent / 'src/robot'
+from pathlib import Path
def get_variables(path, name=None, version=None):
- return {'INTERPRETER': Interpreter(path, name, version)}
+ return {"INTERPRETER": Interpreter(path, name, version)}
class Interpreter:
@@ -21,92 +18,98 @@ def __init__(self, path, name=None, version=None):
name, version = self._get_name_and_version()
self.name = name
self.version = version
- self.version_info = tuple(int(item) for item in version.split('.'))
+ self.version_info = tuple(int(item) for item in version.split("."))
+ self.src_dir = Path(__file__).parent.parent / "src"
def _get_interpreter(self, path):
- path = path.replace('/', os.sep)
+ path = path.replace("/", os.sep)
return [path] if os.path.exists(path) else path.split()
def _get_name_and_version(self):
try:
- output = subprocess.check_output(self.interpreter + ['-V'],
- stderr=subprocess.STDOUT,
- encoding='UTF-8')
+ output = subprocess.check_output(
+ self.interpreter + ["-V"],
+ stderr=subprocess.STDOUT,
+ encoding="UTF-8",
+ )
except (subprocess.CalledProcessError, FileNotFoundError) as err:
- raise ValueError('Failed to get interpreter version: %s' % err)
+ raise ValueError(f"Failed to get interpreter version: {err}")
name, version = output.split()[:2]
- name = name if 'PyPy' not in output else 'PyPy'
- version = re.match(r'\d+\.\d+\.\d+', version).group()
+ name = name if "PyPy" not in output else "PyPy"
+ version = re.match(r"\d+\.\d+\.\d+", version).group()
return name, version
@property
def os(self):
- for condition, name in [(self.is_linux, 'Linux'),
- (self.is_osx, 'OS X'),
- (self.is_windows, 'Windows')]:
+ for condition, name in [
+ (self.is_linux, "Linux"),
+ (self.is_osx, "OS X"),
+ (self.is_windows, "Windows"),
+ ]:
if condition:
return name
return sys.platform
@property
def output_name(self):
- return '{i.name}-{i.version}-{i.os}'.format(i=self).replace(' ', '')
+ return f"{self.name}-{self.version}-{self.os}".replace(" ", "")
@property
def excludes(self):
if self.is_pypy:
- yield 'require-lxml'
- for require in [(3, 7), (3, 8), (3, 9), (3, 10)]:
+ yield "no-pypy"
+ yield "require-lxml"
+ for require in [(3, 9), (3, 10), (3, 14)]:
if self.version_info < require:
- yield 'require-py%d.%d' % require
+ yield "require-py%d.%d" % require
if self.is_windows:
- yield 'no-windows'
+ yield "no-windows"
if not self.is_windows:
- yield 'require-windows'
+ yield "require-windows"
if self.is_osx:
- yield 'no-osx'
+ yield "no-osx"
if not self.is_linux:
- yield 'require-linux'
+ yield "require-linux"
@property
def is_python(self):
- return self.name == 'Python'
+ return self.name == "Python"
@property
def is_pypy(self):
- return self.name == 'PyPy'
+ return self.name == "PyPy"
@property
def is_linux(self):
- return 'linux' in sys.platform
+ return "linux" in sys.platform
@property
def is_osx(self):
- return sys.platform == 'darwin'
+ return sys.platform == "darwin"
@property
def is_windows(self):
- return os.name == 'nt'
+ return os.name == "nt"
@property
def runner(self):
- return self.interpreter + [str(ROBOT_DIR / 'run.py')]
+ return self.interpreter + [str(self.src_dir / "robot/run.py")]
@property
def rebot(self):
- return self.interpreter + [str(ROBOT_DIR / 'rebot.py')]
+ return self.interpreter + [str(self.src_dir / "robot/rebot.py")]
@property
def libdoc(self):
- return self.interpreter + [str(ROBOT_DIR / 'libdoc.py')]
+ return self.interpreter + [str(self.src_dir / "robot/libdoc.py")]
@property
def testdoc(self):
- return self.interpreter + [str(ROBOT_DIR / 'testdoc.py')]
+ return self.interpreter + [str(self.src_dir / "robot/testdoc.py")]
@property
def underline(self):
- return '-' * len(str(self))
+ return "-" * len(str(self))
def __str__(self):
- return f'{self.name} {self.version} on {self.os}'
+ return f"{self.name} {self.version} on {self.os}"
diff --git a/atest/requirements-run.txt b/atest/requirements-run.txt
index ee5b5278817..4dfae292ecc 100644
--- a/atest/requirements-run.txt
+++ b/atest/requirements-run.txt
@@ -1,2 +1,4 @@
+# Dependencies for the acceptance test runner.
+
jsonschema >= 4.0
xmlschema
diff --git a/atest/requirements.txt b/atest/requirements.txt
index 5f0a324dfcf..5b3ad92adb9 100644
--- a/atest/requirements.txt
+++ b/atest/requirements.txt
@@ -1,16 +1,10 @@
-# External Python modules required by acceptance tests.
+# Dependencies required by acceptance tests.
# See atest/README.rst for more information.
-docutils >= 0.10
pygments
-
pyyaml
-
-# On Linux installing lxml with pip may require compilation and development
-# headers. Alternatively it can be installed using a package manager like
-# `sudo apt-get install python-lxml`.
-lxml; platform_python_implementation == 'CPython'
-
+lxml
pillow >= 7.1.0; platform_system == 'Windows'
+telnetlib-313-and-up; python_version >= '3.13'
-r ../utest/requirements.txt
diff --git a/atest/resources/TestCheckerLibrary.py b/atest/resources/TestCheckerLibrary.py
index 61213d5c8d4..d9f89562245 100644
--- a/atest/resources/TestCheckerLibrary.py
+++ b/atest/resources/TestCheckerLibrary.py
@@ -1,195 +1,271 @@
+import json
import os
import re
+from pathlib import Path
+try:
+ from jsonschema import Draft202012Validator as JSONValidator
+except ImportError:
+ JSONValidator = None
from xmlschema import XMLSchema
-from robot import utils
from robot.api import logger
from robot.libraries.BuiltIn import BuiltIn
-from robot.result import (Break, Continue, Error, ExecutionResultBuilder, For,
- ForIteration, If, IfBranch, Keyword, Result, ResultVisitor,
- Return, TestCase, TestSuite, Try, TryBranch, Var, While,
- WhileIteration)
+from robot.libraries.Collections import Collections
+from robot.result import (
+ Break, Continue, Error, ExecutionResult, ExecutionResultBuilder, For, ForIteration,
+ Group, If, IfBranch, Keyword, Result, ResultVisitor, Return, TestCase, TestSuite,
+ Try, TryBranch, Var, While, WhileIteration
+)
from robot.result.model import Body, Iterations
+from robot.utils import eq, get_error_details, is_truthy, Matcher
from robot.utils.asserts import assert_equal
-class NoSlotsKeyword(Keyword):
+class WithBodyTraversing:
+ body: Body
+
+ def __getitem__(self, index):
+ if isinstance(index, str):
+ index = tuple(int(i) for i in index.split(","))
+ if isinstance(index, (int, slice)):
+ return self.body[index]
+ if isinstance(index, tuple):
+ item = self
+ for i in index:
+ item = item.body[int(i)]
+ return item
+ raise TypeError(f"Invalid index {repr(index)}.")
+
+ @property
+ def keywords(self):
+ return self.body.filter(keywords=True)
+
+ @property
+ def messages(self):
+ return self.body.filter(messages=True)
+
+ @property
+ def non_messages(self):
+ return self.body.filter(messages=False)
+
+
+class ATestKeyword(Keyword, WithBodyTraversing):
+ pass
+
+
+class ATestFor(For, WithBodyTraversing):
pass
-class NoSlotsFor(For):
+class ATestWhile(While, WithBodyTraversing):
pass
-class NoSlotsWhile(While):
+class ATestGroup(Group, WithBodyTraversing):
pass
-class NoSlotsIf(If):
+class ATestIf(If, WithBodyTraversing):
pass
-class NoSlotsTry(Try):
+class ATestTry(Try, WithBodyTraversing):
pass
-class NoSlotsVar(Var):
+class ATestVar(Var, WithBodyTraversing):
pass
-class NoSlotsReturn(Return):
+class ATestReturn(Return, WithBodyTraversing):
pass
-class NoSlotsBreak(Break):
+class ATestBreak(Break, WithBodyTraversing):
pass
-class NoSlotsContinue(Continue):
+class ATestContinue(Continue, WithBodyTraversing):
pass
-class NoSlotsError(Error):
+class ATestError(Error, WithBodyTraversing):
pass
-class NoSlotsBody(Body):
- keyword_class = NoSlotsKeyword
- for_class = NoSlotsFor
- if_class = NoSlotsIf
- try_class = NoSlotsTry
- while_class = NoSlotsWhile
- var_class = NoSlotsVar
- return_class = NoSlotsReturn
- break_class = NoSlotsBreak
- continue_class = NoSlotsContinue
- error_class = NoSlotsError
+class ATestBody(Body):
+ keyword_class = ATestKeyword
+ for_class = ATestFor
+ if_class = ATestIf
+ try_class = ATestTry
+ while_class = ATestWhile
+ group_class = ATestGroup
+ var_class = ATestVar
+ return_class = ATestReturn
+ break_class = ATestBreak
+ continue_class = ATestContinue
+ error_class = ATestError
-class NoSlotsIfBranch(IfBranch):
- body_class = NoSlotsBody
+class ATestIfBranch(IfBranch, WithBodyTraversing):
+ body_class = ATestBody
-class NoSlotsTryBranch(TryBranch):
- body_class = NoSlotsBody
+class ATestTryBranch(TryBranch, WithBodyTraversing):
+ body_class = ATestBody
-class NoSlotsForIteration(ForIteration):
- body_class = NoSlotsBody
+class ATestForIteration(ForIteration, WithBodyTraversing):
+ body_class = ATestBody
-class NoSlotsWhileIteration(WhileIteration):
- body_class = NoSlotsBody
+class ATestWhileIteration(WhileIteration, WithBodyTraversing):
+ body_class = ATestBody
-class NoSlotsIterations(Iterations):
- keyword_class = NoSlotsKeyword
+class ATestIterations(Iterations, WithBodyTraversing):
+ keyword_class = ATestKeyword
-NoSlotsKeyword.body_class = NoSlotsVar.body_class = NoSlotsReturn.body_class \
- = NoSlotsBreak.body_class = NoSlotsContinue.body_class \
- = NoSlotsError.body_class = NoSlotsBody
-NoSlotsFor.iterations_class = NoSlotsWhile.iterations_class = NoSlotsIterations
-NoSlotsFor.iteration_class = NoSlotsForIteration
-NoSlotsWhile.iteration_class = NoSlotsWhileIteration
-NoSlotsIf.branch_class = NoSlotsIfBranch
-NoSlotsTry.branch_class = NoSlotsTryBranch
+ATestKeyword.body_class = ATestVar.body_class = ATestReturn.body_class \
+ = ATestBreak.body_class = ATestContinue.body_class \
+ = ATestError.body_class = ATestGroup.body_class \
+ = ATestBody # fmt: skip
+ATestFor.iterations_class = ATestWhile.iterations_class = ATestIterations
+ATestFor.iteration_class = ATestForIteration
+ATestWhile.iteration_class = ATestWhileIteration
+ATestIf.branch_class = ATestIfBranch
+ATestTry.branch_class = ATestTryBranch
-class NoSlotsTestCase(TestCase):
- fixture_class = NoSlotsKeyword
- body_class = NoSlotsBody
+class ATestTestCase(TestCase, WithBodyTraversing):
+ fixture_class = ATestKeyword
+ body_class = ATestBody
-class NoSlotsTestSuite(TestSuite):
- fixture_class = NoSlotsKeyword
- test_class = NoSlotsTestCase
+class ATestTestSuite(TestSuite):
+ fixture_class = ATestKeyword
+ test_class = ATestTestCase
class TestCheckerLibrary:
- ROBOT_LIBRARY_SCOPE = 'GLOBAL'
+ ROBOT_LIBRARY_SCOPE = "GLOBAL"
def __init__(self):
- self.schema = XMLSchema('doc/schema/robot.xsd')
+ self.xml_schema = XMLSchema("doc/schema/result.xsd")
+ self.json_schema = self._load_json_schema()
+
+ def _load_json_schema(self):
+ if not JSONValidator:
+ return None
+ with open("doc/schema/result.json", encoding="UTF-8") as f:
+ return JSONValidator(json.load(f))
- def process_output(self, path, validate=None):
+ def process_output(self, path: "None|Path", validate: "bool|None" = None):
set_suite_variable = BuiltIn().set_suite_variable
- if not path or path.upper() == 'NONE':
- set_suite_variable('$SUITE', None)
+ if path is None:
+ set_suite_variable("$SUITE", None)
logger.info("Not processing output.")
return
- path = path.replace('/', os.sep)
if validate is None:
- validate = os.getenv('ATEST_VALIDATE_OUTPUT', False)
- if utils.is_truthy(validate):
- self._validate_output(path)
+ validate = is_truthy(os.getenv("ATEST_VALIDATE_OUTPUT", False))
+ if validate:
+ if path.suffix.lower() == ".json":
+ self.validate_json_output(path)
+ else:
+ self._validate_output(path)
try:
- logger.info("Processing output '%s'." % path)
- result = Result(root_suite=NoSlotsTestSuite())
- ExecutionResultBuilder(path).build(result)
- except:
- set_suite_variable('$SUITE', None)
- msg, details = utils.get_error_details()
+ logger.info(f"Processing output '{path}'.")
+ if path.suffix.lower() == ".json":
+ result = self._build_result_from_json(path)
+ else:
+ result = self._build_result_from_xml(path)
+ except Exception:
+ set_suite_variable("$SUITE", None)
+ msg, details = get_error_details()
logger.info(details)
- raise RuntimeError('Processing output failed: %s' % msg)
+ raise RuntimeError(f"Processing output failed: {msg}")
result.visit(ProcessResults())
- set_suite_variable('$SUITE', result.suite)
- set_suite_variable('$STATISTICS', result.statistics)
- set_suite_variable('$ERRORS', result.errors)
+ set_suite_variable("$SUITE", result.suite)
+ set_suite_variable("$STATISTICS", result.statistics)
+ set_suite_variable("$ERRORS", result.errors)
+
+ def _build_result_from_xml(self, path):
+ result = Result(source=path, suite=ATestTestSuite())
+ ExecutionResultBuilder(path).build(result)
+ return result
+
+ def _build_result_from_json(self, path):
+ result = Result.from_json(path)
+ result.suite = ATestTestSuite.from_dict(result.suite.to_dict())
+ return result
def _validate_output(self, path):
- schema_version = self._get_schema_version(path)
- if schema_version != self.schema.version:
- raise AssertionError(
- 'Incompatible schema versions. Schema has `version="%s"` '
- 'but output file has `schemaversion="%s"`.'
- % (self.schema.version, schema_version)
- )
- self.schema.validate(path)
+ version = self._get_schema_version(path)
+ if not version:
+ raise ValueError("Schema version not found from XML output.")
+ if version != self.xml_schema.version:
+ raise ValueError(
+ f"Incompatible schema versions. "
+ f'Schema has `version="{self.xml_schema.version}"` but '
+ f'output file has `schemaversion="{version}"`.'
+ )
+ self.xml_schema.validate(path)
def _get_schema_version(self, path):
- with open(path, encoding='UTF-8') as f:
- for line in f:
- if line.startswith(' TestSuite:
- from_source = {'xml': TestSuite.from_xml,
- 'json': TestSuite.from_json}[output.rsplit('.')[-1].lower()]
- return from_source(output)
+ def outputs_should_contain_same_data(
+ self,
+ output1,
+ output2,
+ ignore_timestamps=False,
+ ):
+ dictionaries_should_be_equal = Collections().dictionaries_should_be_equal
+ if ignore_timestamps:
+ ignore_keys = ["start_time", "end_time", "elapsed_time", "timestamp"]
+ else:
+ ignore_keys = None
+ result1 = ExecutionResult(output1)
+ result2 = ExecutionResult(output2)
+ dictionaries_should_be_equal(
+ result1.suite.to_dict(),
+ result2.suite.to_dict(),
+ ignore_keys=ignore_keys,
+ )
+ dictionaries_should_be_equal(
+ result1.statistics.to_dict(),
+ result2.statistics.to_dict(),
+ ignore_keys=ignore_keys,
+ )
+ # Use `zip(..., strict=True)` when Python 3.10 is minimum version.
+ assert len(result1.errors) == len(result2.errors)
+ for msg1, msg2 in zip(result1.errors, result2.errors):
+ dictionaries_should_be_equal(
+ msg1.to_dict(),
+ msg2.to_dict(),
+ ignore_keys=ignore_keys,
+ )
class ProcessResults(ResultVisitor):
- def start_test(self, test):
- for status in 'FAIL', 'SKIP', 'PASS':
+ def visit_test(self, test):
+ for status in "FAIL", "SKIP", "PASS":
if status in test.doc:
test.exp_status = status
test.exp_message = test.doc.split(status, 1)[1].lstrip()
break
else:
- test.exp_status = 'PASS'
- test.exp_message = ''
- test.kws = list(test.body)
-
- def start_body_item(self, item):
- # TODO: Consider not setting these attributes to avoid "NoSlots" variants.
- # - Using normal `body` and `messages` would in general be cleaner.
- # - If `kws` is preserved, it should only contain keywords, not controls.
- # - `msgs` isn't that much shorter than `messages`.
- item.kws = item.body.filter(messages=False)
- item.msgs = item.body.filter(messages=True)
-
- def visit_message(self, message):
- pass
-
- def visit_errors(self, errors):
- errors.msgs = errors.messages
+ test.exp_status = "PASS"
+ test.exp_message = ""
diff --git a/atest/resources/TestHelper.py b/atest/resources/TestHelper.py
index 053c0965642..14b28af9bb9 100644
--- a/atest/resources/TestHelper.py
+++ b/atest/resources/TestHelper.py
@@ -1,5 +1,5 @@
import os
-from stat import S_IREAD, S_IWRITE, S_IEXEC
+from stat import S_IEXEC, S_IREAD, S_IWRITE
from robot.api import logger
@@ -20,18 +20,19 @@ def remove_permissions(self, path):
def file_should_have_correct_line_separators(self, output, sep=os.linesep):
if os.path.isfile(output):
- with open(output, 'rb') as infile:
- output = infile.read().decode('UTF-8')
+ with open(output, "rb") as infile:
+ output = infile.read().decode("UTF-8")
if sep not in output:
- self._wrong_separators('Output has no %r separators' % sep, output)
- extra_r = output.replace(sep, '').count('\r')
- extra_n = output.replace(sep, '').count('\n')
+ self._wrong_separators(f"Output has no {sep!r} separators", output)
+ extra_r = output.replace(sep, "").count("\r")
+ extra_n = output.replace(sep, "").count("\n")
if extra_r or extra_n:
- self._wrong_separators("Output has %d extra \\r and %d extra \\n"
- % (extra_r, extra_n), output)
+ self._wrong_separators(
+ rf"Output has {extra_r} extra \r and {extra_n} extra \n", output
+ )
def _wrong_separators(self, message, output):
- logger.info(repr(output).replace('\\n', '\\n\n'))
+ logger.info(repr(output).replace("\\n", "\\n\n"))
failure = AssertionError(message)
failure.ROBOT_CONTINUE_ON_FAILURE = True
raise failure
diff --git a/atest/resources/atest_resource.robot b/atest/resources/atest_resource.robot
index ed84a5873b2..db41318b1f6 100644
--- a/atest/resources/atest_resource.robot
+++ b/atest/resources/atest_resource.robot
@@ -110,18 +110,35 @@ Check Test Doc
Check Test Tags
[Arguments] ${name} @{expected}
${tc} = Check Test Case ${name}
- Should Contain Tags ${tc} @{expected}
+ Should Have Tags ${tc} @{expected}
RETURN ${tc}
+Check Body Item Data
+ [Arguments] ${item} ${type}=KEYWORD ${status}=PASS ${message}= ${children}=-1 &{others}
+ FOR ${key} ${expected} IN type=${type} status=${status} type=${type} message=${message} &{others}
+ IF $key == 'status' and $type == 'MESSAGE' CONTINUE
+ VAR ${actual} ${item.${key}}
+ IF isinstance($actual, collections.abc.Iterable) and not isinstance($actual, str)
+ Should Be Equal ${{', '.join($actual)}} ${expected}
+ ELSE
+ Should Be Equal ${actual} ${expected}
+ END
+ END
+ IF ${children} >= 0
+ ... Length Should Be ${item.body} ${children}
+
Check Keyword Data
- [Arguments] ${kw} ${name} ${assign}= ${args}= ${status}=PASS ${tags}= ${doc}=* ${type}=KEYWORD
+ [Arguments] ${kw} ${name} ${assign}= ${args}= ${status}=PASS ${tags}= ${doc}=* ${message}=* ${type}=KEYWORD ${children}=-1
Should Be Equal ${kw.full_name} ${name}
Should Be Equal ${{', '.join($kw.assign)}} ${assign}
Should Be Equal ${{', '.join($kw.args)}} ${args}
Should Be Equal ${kw.status} ${status}
Should Be Equal ${{', '.join($kw.tags)}} ${tags}
Should Match ${kw.doc} ${doc}
+ Should Match ${kw.message} ${message}
Should Be Equal ${kw.type} ${type}
+ IF ${children} >= 0
+ ... Length Should Be ${kw.body} ${children}
Check TRY Data
[Arguments] ${try} ${patterns}= ${pattern_type}=${None} ${assign}=${None} ${status}=PASS
@@ -410,4 +427,6 @@ Traceback Should Be
${path} = Normalize Path ${DATADIR}/${path}
${exp} = Set Variable ${exp}\n${SPACE*2}File "${path}", line *, in ${func}\n${SPACE*4}${text}
END
+ # Remove '~~~^^^' lines.
+ ${msg.message} = Evaluate '\\n'.join(line for line in $msg.message.splitlines() if line.strip('~^ ') or not line)
Check Log Message ${msg} ${exp}\n${error} DEBUG pattern=True traceback=True
diff --git a/atest/resources/atest_variables.py b/atest/resources/atest_variables.py
index 113ef9bde1e..cf4b4136f2e 100644
--- a/atest/resources/atest_variables.py
+++ b/atest/resources/atest_variables.py
@@ -1,25 +1,39 @@
import locale
import os
import subprocess
+import sys
from datetime import datetime, timedelta
from os.path import abspath, dirname, join, normpath
import robot
-
-__all__ = ['ROBOTPATH', 'ROBOT_VERSION', 'DATADIR', 'SYSTEM_ENCODING',
- 'CONSOLE_ENCODING', 'datetime', 'timedelta']
+__all__ = [
+ "ROBOTPATH",
+ "ROBOT_VERSION",
+ "DATADIR",
+ "SYSTEM_ENCODING",
+ "CONSOLE_ENCODING",
+ "datetime",
+ "timedelta",
+]
ROBOTPATH = dirname(abspath(robot.__file__))
ROBOT_VERSION = robot.version.get_version()
-DATADIR = normpath(join(dirname(abspath(__file__)), '..', 'testdata'))
+DATADIR = normpath(join(dirname(abspath(__file__)), "..", "testdata"))
-SYSTEM_ENCODING = locale.getpreferredencoding(False)
+if sys.version_info >= (3, 11):
+ SYSTEM_ENCODING = locale.getencoding()
+else:
+ SYSTEM_ENCODING = locale.getpreferredencoding(False)
# Python 3.6+ uses UTF-8 internally on Windows. We want real console encoding.
-if os.name == 'nt':
- output = subprocess.check_output('chcp', shell=True, encoding='ASCII',
- errors='ignore')
- CONSOLE_ENCODING = 'cp' + output.split()[-1]
+if os.name == "nt":
+ output = subprocess.check_output(
+ "chcp",
+ shell=True,
+ encoding="ASCII",
+ errors="ignore",
+ )
+ CONSOLE_ENCODING = "cp" + output.split()[-1]
else:
CONSOLE_ENCODING = locale.getlocale()[-1]
diff --git a/atest/resources/unicode_vars.py b/atest/resources/unicode_vars.py
index ac438bee7fd..00b35f9e162 100644
--- a/atest/resources/unicode_vars.py
+++ b/atest/resources/unicode_vars.py
@@ -1,12 +1,14 @@
-message_list = ['Circle is 360\u00B0',
- 'Hyv\u00E4\u00E4 \u00FC\u00F6t\u00E4',
- '\u0989\u09C4 \u09F0 \u09FA \u099F \u09EB \u09EA \u09B9']
+message_list = [
+ "Circle is 360\xb0",
+ "Hyv\xe4\xe4 \xfc\xf6t\xe4",
+ "\u0989\u09c4 \u09f0 \u09fa \u099f \u09eb \u09ea \u09b9",
+]
message1 = message_list[0]
message2 = message_list[1]
message3 = message_list[2]
-messages = ', '.join(message_list)
+messages = ", ".join(message_list)
sect = chr(167)
auml = chr(228)
diff --git a/atest/robot/cli/console/colors_and_width.robot b/atest/robot/cli/console/colors_and_width.robot
index 9a6680afe80..e9fbbeb2772 100644
--- a/atest/robot/cli/console/colors_and_width.robot
+++ b/atest/robot/cli/console/colors_and_width.robot
@@ -5,20 +5,20 @@ Resource console_resource.robot
*** Test Cases ***
Console Colors Auto
- Run Tests With Colors --consolecolors auto
- Outputs should not have ANSI colors
+ Run Tests With Warnings --consolecolors auto
+ Outputs should not have ANSI codes
Console Colors Off
- Run Tests With Colors --consolecolors OFF
- Outputs should not have ANSI colors
+ Run Tests With Warnings --consolecolors OFF
+ Outputs should not have ANSI codes
Console Colors On
- Run Tests With Colors --ConsoleCol on
+ Run Tests With Warnings --ConsoleCol on
Outputs should have ANSI colors when not on Windows
Console Colors ANSI
- Run Tests With Colors --Console-Colors AnSi
- Outputs should have ANSI colors
+ Run Tests With Warnings --Console-Colors AnSi
+ Outputs should have ANSI codes
Invalid Console Colors
Run Tests Without Processing Output -C InVaLid misc/pass_and_fail.robot
@@ -43,27 +43,43 @@ Invalid Width
Run Tests Without Processing Output -W InVaLid misc/pass_and_fail.robot
Stderr Should Be Equal To [ ERROR ] Invalid value for option '--consolewidth': Expected integer, got 'InVaLid'.${USAGE TIP}\n
+Console links off
+ [Documentation] Console links being enabled is tested as part of testing console colors.
+ Run Tests With Warnings --console-links oFF --console-colors on
+ Outputs should have ANSI colors when not on Windows links=False
+
+Invalid link config
+ Run Tests Without Processing Output --ConsoleLinks InVaLid misc/pass_and_fail.robot
+ Stderr Should Be Equal To [ ERROR ] Invalid console link value 'InVaLid. Available 'AUTO' and 'OFF'.${USAGE TIP}\n
+
*** Keywords ***
-Run Tests With Colors
- [Arguments] ${colors}
- Run Tests ${colors} --variable LEVEL1:WARN misc/pass_and_fail.robot
+Run Tests With Warnings
+ [Arguments] ${options}
+ Run Tests ${options} --variable LEVEL1:WARN misc/pass_and_fail.robot
-Outputs should not have ANSI colors
+Outputs should not have ANSI codes
Stdout Should Contain | PASS |
Stdout Should Contain | FAIL |
Stderr Should Contain [ WARN ]
Outputs should have ANSI colors when not on Windows
+ [Arguments] ${links}=True
IF os.sep == '/'
- Outputs should have ANSI colors
+ Outputs should have ANSI codes ${links}
ELSE
- Outputs should not have ANSI colors
+ Outputs should not have ANSI codes
END
-Outputs should have ANSI colors
+Outputs should have ANSI codes
+ [Arguments] ${links}=True
Stdout Should Not Contain | PASS |
Stdout Should Not Contain | FAIL |
Stderr Should Not Contain [ WARN ]
- Stdout Should Contain PASS
- Stdout Should Contain FAIL
- Stderr Should Contain WARN
+ Stdout Should Contain | \x1b[32mPASS\x1b[0m |
+ Stdout Should Contain | \x1b[31mFAIL\x1b[0m |
+ Stderr Should Contain [ \x1b[33mWARN\x1b[0m ]
+ IF ${links}
+ Stdout Should Contain \x1b]8;;file://
+ ELSE
+ Stdout Should Not Contain \x1b]8;;file://
+ END
diff --git a/atest/robot/cli/console/disable_standard_streams.py b/atest/robot/cli/console/disable_standard_streams.py
new file mode 100644
index 00000000000..f22de07454a
--- /dev/null
+++ b/atest/robot/cli/console/disable_standard_streams.py
@@ -0,0 +1,4 @@
+import sys
+
+sys.stdin = sys.stdout = sys.stderr = None
+sys.__stdin__ = sys.__stdout__ = sys.__stderr__ = None
diff --git a/atest/robot/cli/console/encoding.robot b/atest/robot/cli/console/encoding.robot
index 3dc94dd6a93..00194b8e242 100644
--- a/atest/robot/cli/console/encoding.robot
+++ b/atest/robot/cli/console/encoding.robot
@@ -26,7 +26,7 @@ PYTHONIOENCODING is honored in console output
Should Contain ${result.stdout} ???-????? T??t ??d K?yw?rd N?m?s, Спасибо${SPACE*29}| PASS |
Invalid encoding configuration
- [Tags] no-windows no-osx
+ [Tags] no-windows no-osx no-pypy
${cmd} = Join command line
... LANG=invalid
... LC_TYPE=invalid
diff --git a/atest/robot/cli/console/expected_output/ExpectedOutputLibrary.py b/atest/robot/cli/console/expected_output/ExpectedOutputLibrary.py
index f7851a42b6c..d30e9a91ab9 100644
--- a/atest/robot/cli/console/expected_output/ExpectedOutputLibrary.py
+++ b/atest/robot/cli/console/expected_output/ExpectedOutputLibrary.py
@@ -1,34 +1,33 @@
-from os.path import abspath, dirname, join
from fnmatch import fnmatchcase
from operator import eq
+from os.path import abspath, dirname, join
from robot.api import logger
from robot.api.deco import keyword
-
ROBOT_AUTO_KEYWORDS = False
CURDIR = dirname(abspath(__file__))
@keyword
def output_should_be(actual, expected, **replaced):
- actual = _read_file(actual, 'Actual')
- expected = _read_file(join(CURDIR, expected), 'Expected', replaced)
+ actual = _read_file(actual, "Actual")
+ expected = _read_file(join(CURDIR, expected), "Expected", replaced)
if len(expected) != len(actual):
- raise AssertionError('Lengths differ. Expected %d lines but got %d'
- % (len(expected), len(actual)))
+ raise AssertionError(
+ f"Lengths differ. Expected {len(expected)} lines, got {len(actual)}."
+ )
for exp, act in zip(expected, actual):
- tester = fnmatchcase if '*' in exp else eq
+ tester = fnmatchcase if "*" in exp else eq
if not tester(act.rstrip(), exp.rstrip()):
- raise AssertionError('Lines differ.\nExpected: %s\nActual: %s'
- % (exp, act))
+ raise AssertionError(f"Lines differ.\nExpected: {exp}\nActual: {act}")
def _read_file(path, title, replaced=None):
- with open(path) as file:
+ with open(path, encoding="UTF-8") as file:
content = file.read()
if replaced:
for item in replaced:
content = content.replace(item, replaced[item])
- logger.debug('%s:\n%s' % (title, content))
+ logger.debug(f"{title}:\n{content}")
return content.splitlines()
diff --git a/atest/robot/cli/console/max_assign_length.robot b/atest/robot/cli/console/max_assign_length.robot
index 8d9e866805b..587c9f1b86f 100644
--- a/atest/robot/cli/console/max_assign_length.robot
+++ b/atest/robot/cli/console/max_assign_length.robot
@@ -4,7 +4,7 @@ Test Template Assignment messages should be
Resource atest_resource.robot
*** Variables ***
-@{TESTS} 10 chars 200 chars 201 chars 1000 chars 1001 chars
+@{TESTS} 10 chars 200 chars 201 chars 1000 chars 1001 chars VAR
*** Test Cases ***
Default limit
@@ -14,6 +14,7 @@ Default limit
... '0123456789' * 20 + '...'
... '0123456789' * 20 + '...'
... '0123456789' * 20 + '...'
+ ... '0123456789' * 20 + '...'
Custom limit
10
@@ -22,18 +23,21 @@ Custom limit
... '0123456789' + '...'
... '0123456789' + '...'
... '0123456789' + '...'
+ ... '0123456789' + '...'
1000
... '0123456789'
... '0123456789' * 20
... '0123456789' * 20 + '0'
... '0123456789' * 100
... '0123456789' * 100 + '...'
+ ... '0123456789' * 100
10000
... '0123456789'
... '0123456789' * 20
... '0123456789' * 20 + '0'
... '0123456789' * 100
... '0123456789' * 100 + '0'
+ ... '0123456789' * 100
Hide value
0
@@ -42,12 +46,14 @@ Hide value
... '...'
... '...'
... '...'
+ ... '...'
-666
... '...'
... '...'
... '...'
... '...'
... '...'
+ ... '...'
Invalid
[Template] NONE
@@ -65,9 +71,8 @@ Assignment messages should be
ELSE
Run Tests ${EMPTY} cli/console/max_assign_length.robot
END
- Length Should Be ${messages} 5
- FOR ${name} ${msg} IN ZIP ${TESTS} ${MESSAGES}
+ FOR ${name} ${msg} IN ZIP ${TESTS} ${messages} mode=STRICT
${tc} = Check Test Case ${name}
${msg} = Evaluate ${msg}
- Check Log Message ${tc.body[0].messages[0]} \${value} = ${msg}
+ Check Log Message ${tc[0, 0]} \${value} = ${msg}
END
diff --git a/atest/robot/cli/console/max_error_lines.robot b/atest/robot/cli/console/max_error_lines.robot
index cc5140066f7..a2790f577cb 100644
--- a/atest/robot/cli/console/max_error_lines.robot
+++ b/atest/robot/cli/console/max_error_lines.robot
@@ -38,15 +38,16 @@ Has Been Cut
Should Contain ${test.message} ${EXPLANATION}
Should Match Non Empty Regexp ${test.message} ${eol_dots}
Should Match Non Empty Regexp ${test.message} ${bol_dots}
- Error Message In Log Should Not Have Been Cut ${test.kws}
+ Error Message In Log Should Not Have Been Cut ${test.body}
RETURN ${test}
Error Message In Log Should Not Have Been Cut
- [Arguments] ${kws}
- @{keywords} = Set Variable ${kws}
- FOR ${kw} IN @{keywords}
- Run Keyword If ${kw.msgs} Should Not Contain ${kw.msgs[-1].message} ${EXPLANATION}
- Error Message In Log Should Not Have Been Cut ${kw.kws}
+ [Arguments] ${items}
+ FOR ${item} IN @{items}
+ FOR ${msg} IN @{item.messages}
+ Should Not Contain ${msg.message} ${EXPLANATION}
+ END
+ Error Message In Log Should Not Have Been Cut ${item.non_messages}
END
Should Match Non Empty Regexp
diff --git a/atest/robot/cli/console/piping.py b/atest/robot/cli/console/piping.py
index 1ed0ebb6e25..9386a0d2d33 100644
--- a/atest/robot/cli/console/piping.py
+++ b/atest/robot/cli/console/piping.py
@@ -4,14 +4,14 @@
def read_all():
fails = 0
for line in sys.stdin:
- if 'FAIL' in line:
+ if "FAIL" in line:
fails += 1
- print("%d lines with 'FAIL' found!" % fails)
+ print(f"{fails} lines with 'FAIL' found!")
def read_some():
for line in sys.stdin:
- if 'FAIL' in line:
+ if "FAIL" in line:
print("Line with 'FAIL' found!")
sys.stdin.close()
break
diff --git a/atest/robot/cli/console/piping.robot b/atest/robot/cli/console/piping.robot
index ca5963844c8..15a50291753 100644
--- a/atest/robot/cli/console/piping.robot
+++ b/atest/robot/cli/console/piping.robot
@@ -14,7 +14,7 @@ ${TARGET} ${CURDIR}${/}piping.py
*** Test Cases ***
Pipe to command consuming all data
Run with pipe and validate results read_all
- Should Be Equal ${STDOUT} 17 lines with 'FAIL' found!
+ Should Be Equal ${STDOUT} 20 lines with 'FAIL' found!
Pipe to command consuming some data
Run with pipe and validate results read_some
@@ -28,8 +28,7 @@ Pipe to command consuming no data
Run with pipe and validate results
[Arguments] ${pipe style}
${command} = Join Command Line @{COMMAND}
- ${result} = Run Process ${command} | python ${TARGET} ${pipe style}
- ... shell=true
+ ${result} = Run Process ${command} | python ${TARGET} ${pipe style} shell=True
Log Many RC: ${result.rc} STDOUT:\n${result.stdout} STDERR:\n${result.stderr}
Should Be Equal ${result.rc} ${0}
Process Output ${OUTPUT}
diff --git a/atest/robot/cli/console/standard_streams_disabled.robot b/atest/robot/cli/console/standard_streams_disabled.robot
new file mode 100644
index 00000000000..44af45e78b2
--- /dev/null
+++ b/atest/robot/cli/console/standard_streams_disabled.robot
@@ -0,0 +1,23 @@
+*** Settings ***
+Suite Setup Run tests with standard streams disabled
+Resource console_resource.robot
+
+*** Test Cases ***
+Execution succeeds
+ Should Be Equal ${SUITE.name} Log
+
+Console outputs are disabled
+ Stdout Should Be empty.txt
+ Stderr Should Be empty.txt
+
+Log To Console keyword succeeds
+ Check Test Case Log to console
+
+*** Keywords ***
+Run tests with standard streams disabled
+ [Documentation] Streams are disabled by using the sitecustomize module:
+ ... https://docs.python.org/3/library/site.html#module-sitecustomize
+ Copy File ${CURDIR}/disable_standard_streams.py %{TEMPDIR}/sitecustomize.py
+ Set Environment Variable PYTHONPATH %{TEMPDIR}
+ Run Tests ${EMPTY} standard_libraries/builtin/log.robot
+ [Teardown] Remove File %{TEMPDIR}/sitecustomize.py
diff --git a/atest/robot/cli/dryrun/dryrun.robot b/atest/robot/cli/dryrun/dryrun.robot
index ba99d924477..f6e0c24269c 100644
--- a/atest/robot/cli/dryrun/dryrun.robot
+++ b/atest/robot/cli/dryrun/dryrun.robot
@@ -6,43 +6,51 @@ Resource dryrun_resource.robot
*** Test Cases ***
Passing keywords
${tc}= Check Test Case ${TESTNAME}
- Length Should Be ${tc.kws} 4
- Check Keyword Data ${tc.kws[0]} BuiltIn.Log status=NOT RUN args=Hello from test
- Check Keyword Data ${tc.kws[1]} OperatingSystem.List Directory status=NOT RUN assign=\${contents} args=.
- Check Keyword Data ${tc.kws[2]} resource.Simple UK
- Check Keyword Data ${tc.kws[2].kws[0]} BuiltIn.Log status=NOT RUN args=Hello from UK
+ Length Should Be ${tc.body} 4
+ Check Keyword Data ${tc[0]} BuiltIn.Log status=NOT RUN args=Hello from test
+ Check Keyword Data ${tc[1]} OperatingSystem.List Directory status=NOT RUN assign=\${contents} args=.
+ Check Keyword Data ${tc[2]} resource.Simple UK
+ Check Keyword Data ${tc[2, 0]} BuiltIn.Log status=NOT RUN args=Hello from UK
Keywords with embedded arguments
${tc}= Check Test Case ${TESTNAME}
- Length Should Be ${tc.kws} 5
- Check Keyword Data ${tc.kws[0]} Embedded arguments here
- Check Keyword Data ${tc.kws[0].kws[0]} BuiltIn.No Operation status=NOT RUN
- Check Keyword Data ${tc.kws[1]} Embedded args rock here
- Check Keyword Data ${tc.kws[1].kws[0]} BuiltIn.No Operation status=NOT RUN
- Check Keyword Data ${tc.kws[2]} Some embedded and normal args args=42
- Check Keyword Data ${tc.kws[2].kws[0]} BuiltIn.No Operation status=NOT RUN
- Check Keyword Data ${tc.kws[3]} Some embedded and normal args args=\${does not exist}
- Check Keyword Data ${tc.kws[3].kws[0]} BuiltIn.No Operation status=NOT RUN
+ Length Should Be ${tc.body} 5
+ Check Keyword Data ${tc[0]} Embedded arguments here
+ Check Keyword Data ${tc[0, 0]} BuiltIn.No Operation status=NOT RUN
+ Check Keyword Data ${tc[1]} Embedded args rock here
+ Check Keyword Data ${tc[1, 0]} BuiltIn.No Operation status=NOT RUN
+ Check Keyword Data ${tc[2]} Some embedded and normal args args=42
+ Check Keyword Data ${tc[2, 0]} BuiltIn.No Operation status=NOT RUN
+ Check Keyword Data ${tc[3]} Some embedded and normal args args=\${does not exist}
+ Check Keyword Data ${tc[3, 0]} BuiltIn.No Operation status=NOT RUN
+
+Keywords with types
+ Check Test Case ${TESTNAME}
+
+Keywords with types that would fail
+ Check Test Case ${TESTNAME}
+ Error In File 1 cli/dryrun/dryrun.robot 214
+ ... Creating keyword 'Invalid type' failed: Invalid argument specification: Invalid argument '\${arg: bad}': Unrecognized type 'bad'.
Library keyword with embedded arguments
${tc}= Check Test Case ${TESTNAME}
- Length Should Be ${tc.kws} 2
- Check Keyword Data ${tc.kws[0]} EmbeddedArgs.Log 42 times status=NOT RUN
+ Length Should Be ${tc.body} 2
+ Check Keyword Data ${tc[0]} EmbeddedArgs.Log 42 times status=NOT RUN
Keywords that would fail
${tc}= Check Test Case ${TESTNAME}
- Length Should Be ${tc.kws} 3
- Check Keyword Data ${tc.kws[0]} BuiltIn.Fail status=NOT RUN args=Not actually executed so won't fail.
- Check Keyword Data ${tc.kws[1]} resource.Fail In UK
- Length Should Be ${tc.kws[1].kws} 2
- Check Keyword Data ${tc.kws[1].kws[0]} BuiltIn.Fail status=NOT RUN args=
- Check Keyword Data ${tc.kws[1].kws[1]} BuiltIn.Fail status=NOT RUN args=And again
+ Length Should Be ${tc.body} 3
+ Check Keyword Data ${tc[0]} BuiltIn.Fail status=NOT RUN args=Not actually executed so won't fail.
+ Check Keyword Data ${tc[1]} resource.Fail In UK
+ Length Should Be ${tc[1].body} 2
+ Check Keyword Data ${tc[1, 0]} BuiltIn.Fail status=NOT RUN args=
+ Check Keyword Data ${tc[1, 1]} BuiltIn.Fail status=NOT RUN args=And again
Scalar variables are not checked in keyword arguments
[Documentation] Variables are too often set somehow dynamically that we cannot expect them to always exist.
${tc}= Check Test Case ${TESTNAME}
- Check Keyword Data ${tc.kws[0]} BuiltIn.Log status=NOT RUN args=\${TESTNAME}
- Check Keyword Data ${tc.kws[1]} BuiltIn.Log status=NOT RUN args=\${this does not exist}
+ Check Keyword Data ${tc[0]} BuiltIn.Log status=NOT RUN args=\${TESTNAME}
+ Check Keyword Data ${tc[1]} BuiltIn.Log status=NOT RUN args=\${this does not exist}
List variables are not checked in keyword arguments
[Documentation] See the doc of the previous test
@@ -55,22 +63,22 @@ Dict variables are not checked in keyword arguments
Variables are not checked in when arguments are embedded
[Documentation] See the doc of the previous test
${tc}= Check Test Case ${TESTNAME}
- Check Keyword Data ${tc.kws[0]} Embedded \${TESTNAME} here
- Check Keyword Data ${tc.kws[0].kws[0]} BuiltIn.No Operation status=NOT RUN
- Check Keyword Data ${tc.kws[1]} Embedded \${nonex} here
- Check Keyword Data ${tc.kws[1].kws[0]} BuiltIn.No Operation status=NOT RUN
+ Check Keyword Data ${tc[0]} Embedded \${TESTNAME} here
+ Check Keyword Data ${tc[0, 0]} BuiltIn.No Operation status=NOT RUN
+ Check Keyword Data ${tc[1]} Embedded \${nonex} here
+ Check Keyword Data ${tc[1, 0]} BuiltIn.No Operation status=NOT RUN
Setup/teardown with non-existing variable is ignored
${tc} = Check Test Case ${TESTNAME}
- Setup Should Not Be Defined ${SUITE}
- Setup Should Not Be Defined ${tc}
+ Setup Should Not Be Defined ${SUITE}
+ Setup Should Not Be Defined ${tc}
Teardown Should Not Be Defined ${tc}
Setup/teardown with existing variable is resolved and executed
${tc} = Check Test Case ${TESTNAME}
- Check Keyword Data ${tc.setup} BuiltIn.No Operation status=NOT RUN type=SETUP
- Check Keyword Data ${tc.teardown} Teardown args=\${nonex arg} type=TEARDOWN
- Check Keyword Data ${tc.teardown.body[0]} BuiltIn.Log args=\${arg} status=NOT RUN
+ Check Keyword Data ${tc.setup} BuiltIn.No Operation status=NOT RUN type=SETUP
+ Check Keyword Data ${tc.teardown} Teardown args=\${nonex arg} type=TEARDOWN
+ Check Keyword Data ${tc.teardown[0]} BuiltIn.Log args=\${arg} status=NOT RUN
User keyword return value
Check Test Case ${TESTNAME}
@@ -80,29 +88,29 @@ Non-existing variable in user keyword return value
Test Setup and Teardown
${tc}= Check Test Case ${TESTNAME}
- Length Should Be ${tc.kws} 2
- Check Keyword Data ${tc.setup} BuiltIn.Log args=Hello Setup status=NOT RUN type=SETUP
- Check Keyword Data ${tc.teardown} Does not exist status=FAIL type=TEARDOWN
+ Length Should Be ${tc.body} 2
+ Check Keyword Data ${tc.setup} BuiltIn.Log args=Hello Setup status=NOT RUN type=SETUP
+ Check Keyword Data ${tc.teardown} Does not exist status=FAIL type=TEARDOWN
Keyword Teardown
${tc}= Check Test Case ${TESTNAME}
- Length Should Be ${tc.kws} 2
- Check Keyword Data ${tc.kws[0].teardown} Does not exist status=FAIL type=TEARDOWN
+ Length Should Be ${tc.body} 2
+ Check Keyword Data ${tc[0].teardown} Does not exist status=FAIL type=TEARDOWN
Keyword teardown with non-existing variable is ignored
Check Test Case ${TESTNAME}
Keyword teardown with existing variable is resolved and executed
${tc}= Check Test Case ${TESTNAME}
- Check Keyword Data ${tc.kws[0].teardown} Teardown args=\${I DO NOT EXIST} type=TEARDOWN
- Check Keyword Data ${tc.kws[0].teardown.kws[0]} BuiltIn.Log args=\${arg} status=NOT RUN
+ Check Keyword Data ${tc[0].teardown} Teardown args=\${I DO NOT EXIST} type=TEARDOWN
+ Check Keyword Data ${tc[0].teardown[0]} BuiltIn.Log args=\${arg} status=NOT RUN
Non-existing keyword name
Check Test Case ${TESTNAME}
Invalid syntax in UK
Check Test Case ${TESTNAME}
- Error In File 0 cli/dryrun/dryrun.robot 167
+ Error In File 0 cli/dryrun/dryrun.robot 210
... SEPARATOR=\n
... Creating keyword 'Invalid Syntax UK' failed: Invalid argument specification: Multiple errors:
... - Invalid argument syntax '\${oops'.
@@ -113,19 +121,19 @@ Multiple Failures
Avoid keyword in dry-run
${tc} = Check Test Case ${TESTNAME}
- Keyword should have been skipped with tag ${tc.kws[0]} Keyword not run in dry-run robot:no-dry-run
- Keyword should have been skipped with tag ${tc.kws[1]} Another keyword not run in dry-run ROBOT: no-dry-run
- Keyword should have been skipped with tag ${tc.kws[2].kws[0]} Keyword not run in dry-run robot:no-dry-run
- Keyword should have been skipped with tag ${tc.kws[2].kws[1]} Another keyword not run in dry-run ROBOT: no-dry-run
- Keyword should have been validated ${tc.kws[2].kws[2]}
- Keyword should have been validated ${tc.kws[3]}
+ Keyword should have been skipped with tag ${tc[0]} Keyword not run in dry-run robot:no-dry-run
+ Keyword should have been skipped with tag ${tc[1]} Another keyword not run in dry-run ROBOT: no-dry-run
+ Keyword should have been skipped with tag ${tc[2, 0]} Keyword not run in dry-run robot:no-dry-run
+ Keyword should have been skipped with tag ${tc[2, 1]} Another keyword not run in dry-run ROBOT: no-dry-run
+ Keyword should have been validated ${tc[2, 2]}
+ Keyword should have been validated ${tc[3]}
Invalid imports
- Error in file 1 cli/dryrun/dryrun.robot 7
+ Error in file 2 cli/dryrun/dryrun.robot 7
... Importing library 'DoesNotExist' failed: *Error: *
- Error in file 2 cli/dryrun/dryrun.robot 8
+ Error in file 3 cli/dryrun/dryrun.robot 8
... Variable file 'wrong_path.py' does not exist.
- Error in file 3 cli/dryrun/dryrun.robot 9
+ Error in file 4 cli/dryrun/dryrun.robot 9
... Resource file 'NonExisting.robot' does not exist.
[Teardown] NONE
diff --git a/atest/robot/cli/dryrun/dryrun_resource.robot b/atest/robot/cli/dryrun/dryrun_resource.robot
index 0c87de0da49..3591a4168fd 100644
--- a/atest/robot/cli/dryrun/dryrun_resource.robot
+++ b/atest/robot/cli/dryrun/dryrun_resource.robot
@@ -4,14 +4,13 @@ Resource atest_resource.robot
*** Keywords ***
Keyword should have been skipped with tag
[Arguments] ${kw} ${name} ${tags}
- Check Keyword Data ${kw} ${name} status=PASS tags=${tags}
- Should Be Empty ${kw.kws}
+ Check Keyword Data ${kw} ${name} status=PASS tags=${tags} children=0
Keyword should have been validated
[Arguments] ${kw}
- Check Keyword Data ${kw} resource.This is validated
- Check Keyword Data ${kw.kws[0]} BuiltIn.Log status=NOT RUN args=This is validated
+ Check Keyword Data ${kw} resource.This is validated
+ Check Keyword Data ${kw[0]} BuiltIn.Log status=NOT RUN args=This is validated
Last keyword should have been validated
- ${tc} = Get test case ${TEST NAME}
- Keyword should have been validated ${tc.kws[-1]}
+ ${tc} = Get Test Case ${TEST NAME}
+ Keyword should have been validated ${tc[-1]}
diff --git a/atest/robot/cli/dryrun/executed_builtin_keywords.robot b/atest/robot/cli/dryrun/executed_builtin_keywords.robot
index a8e1df246f1..7aa269847e3 100644
--- a/atest/robot/cli/dryrun/executed_builtin_keywords.robot
+++ b/atest/robot/cli/dryrun/executed_builtin_keywords.robot
@@ -4,18 +4,28 @@ Resource atest_resource.robot
*** Test Cases ***
Import Library
- Check Test Case ${TESTNAME}
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Keyword Data ${tc[0]} BuiltIn.Import Library args=String
Syslog Should Contain Imported library 'String' with arguments [ ]
Syslog Should Contain Imported library 'ParameterLibrary' with arguments [ value | 42 ]
+Import Resource
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Keyword Data ${tc[0]} BuiltIn.Import Resource args=\${RESOURCE}
+ ${resource} = Normalize Path ${DATADIR}/cli/dryrun/resource.robot
+ Syslog Should Contain Imported resource file '${resource}' (6 keywords).
+
Set Library Search Order
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.kws[1].full_name} Second.Parameters
- Should Be Equal ${tc.kws[2].full_name} First.Parameters
- Should Be Equal ${tc.kws[4].full_name} Dynamic.Parameters
+ Check Keyword Data ${tc[0]} BuiltIn.Set Library Search Order args=Second
+ Should Be Equal ${tc[1].full_name} Second.Parameters
+ Should Be Equal ${tc[2].full_name} First.Parameters
+ Should Be Equal ${tc[4].full_name} Dynamic.Parameters
Set Tags
- Check Test Tags ${TESTNAME} \${2} \${var} Tag0 Tag1 Tag2
+ ${tc} = Check Test Tags ${TESTNAME} \${2} \${var} Tag0 Tag1 Tag2
+ Check Keyword Data ${tc[0]} BuiltIn.Set Tags args=Tag1, Tag2, \${var}, \${2}
Remove Tags
- Check Test Tags ${TESTNAME} Tag1 Tag3
+ ${tc} = Check Test Tags ${TESTNAME} Tag1 Tag3
+ Check Keyword Data ${tc[0]} BuiltIn.Remove Tags args=Tag2, \${var}
diff --git a/atest/robot/cli/dryrun/for.robot b/atest/robot/cli/dryrun/for.robot
index 0b34d8d1c91..879609609d5 100644
--- a/atest/robot/cli/dryrun/for.robot
+++ b/atest/robot/cli/dryrun/for.robot
@@ -6,11 +6,11 @@ Resource dryrun_resource.robot
*** Test Cases ***
FOR
${tc} = Check Test Case ${TESTNAME}
- Validate loops ${tc} 4
- Length should be ${tc.kws[2].kws} 3
- Length should be ${tc.kws[2].kws[0].kws} 0
- Length should be ${tc.kws[2].kws[1].kws} 1
- Length should be ${tc.kws[2].kws[2].kws} 0
+ Validate loops ${tc} 4
+ Length should be ${tc[2].body} 3
+ Length should be ${tc[2, 0].body} 0
+ Length should be ${tc[2, 1].body} 1
+ Length should be ${tc[2, 2].body} 0
FOR IN RANGE
${tc} = Check Test Case ${TESTNAME}
@@ -27,8 +27,8 @@ FOR IN ZIP
*** Keywords ***
Validate loops
[Arguments] ${tc} ${kws}=3
- Length should be ${tc.kws} ${kws}
- Length should be ${tc.kws[0].kws} 1
- Length should be ${tc.kws[0].kws[0].kws} 2
- Length should be ${tc.kws[1].kws} 1
- Length should be ${tc.kws[1].kws[0].kws} 1
+ Length should be ${tc.body} ${kws}
+ Length should be ${tc[0].body} 1
+ Length should be ${tc[0, 0].body} 2
+ Length should be ${tc[1].body} 1
+ Length should be ${tc[1, 0].body} 1
diff --git a/atest/robot/cli/dryrun/if.robot b/atest/robot/cli/dryrun/if.robot
index 24db7db4b32..31bf7359c26 100644
--- a/atest/robot/cli/dryrun/if.robot
+++ b/atest/robot/cli/dryrun/if.robot
@@ -6,18 +6,18 @@ Resource dryrun_resource.robot
*** Test Cases ***
IF will not recurse in dry run
${tc}= Check Test Case ${TESTNAME}
- Check Branch Statuses ${tc.body[0]} Recursive if PASS
- Check Branch Statuses ${tc.body[0].body[0].body[0].body[0]} Recursive if NOT RUN
+ Check Branch Statuses ${tc[0]} Recursive if PASS
+ Check Branch Statuses ${tc[0, 0, 0, 0]} Recursive if NOT RUN
ELSE IF will not recurse in dry run
${tc}= Check Test Case ${TESTNAME}
- Check Branch Statuses ${tc.body[0]} Recursive else if PASS
- Check Branch Statuses ${tc.body[0].body[0].body[1].body[0]} Recursive else if NOT RUN
+ Check Branch Statuses ${tc[0]} Recursive else if PASS
+ Check Branch Statuses ${tc[0, 0, 1, 0]} Recursive else if NOT RUN
ELSE will not recurse in dry run
${tc}= Check Test Case ${TESTNAME}
- Check Branch Statuses ${tc.body[0]} Recursive else PASS
- Check Branch Statuses ${tc.body[0].body[0].body[2].body[0]} Recursive else NOT RUN
+ Check Branch Statuses ${tc[0]} Recursive else PASS
+ Check Branch Statuses ${tc[0, 0, 2, 0]} Recursive else NOT RUN
Dryrun fail inside of IF
Check Test Case ${TESTNAME}
@@ -44,9 +44,9 @@ Dryrun fail empty IF in non executed branch
Check Branch Statuses
[Arguments] ${kw} ${name} ${status}
Should Be Equal ${kw.name} ${name}
- Should Be Equal ${kw.body[0].body[0].type} IF
- Should Be Equal ${kw.body[0].body[0].status} ${status}
- Should Be Equal ${kw.body[0].body[1].type} ELSE IF
- Should Be Equal ${kw.body[0].body[1].status} ${status}
- Should Be Equal ${kw.body[0].body[2].type} ELSE
- Should Be Equal ${kw.body[0].body[2].status} ${status}
+ Should Be Equal ${kw[0, 0].type} IF
+ Should Be Equal ${kw[0, 0].status} ${status}
+ Should Be Equal ${kw[0, 1].type} ELSE IF
+ Should Be Equal ${kw[0, 1].status} ${status}
+ Should Be Equal ${kw[0, 2].type} ELSE
+ Should Be Equal ${kw[0, 2].status} ${status}
diff --git a/atest/robot/cli/dryrun/run_keyword_variants.robot b/atest/robot/cli/dryrun/run_keyword_variants.robot
index fcbbfe40791..8d53a67d722 100644
--- a/atest/robot/cli/dryrun/run_keyword_variants.robot
+++ b/atest/robot/cli/dryrun/run_keyword_variants.robot
@@ -1,108 +1,130 @@
*** Settings ***
-Suite Setup Run Tests --dryrun cli/dryrun/run_keyword_variants.robot
-Resource atest_resource.robot
+Suite Setup Run Tests --dryrun --listener ${LISTENER} cli/dryrun/run_keyword_variants.robot
+Resource atest_resource.robot
+
+*** Variables ***
+${LISTENER} ${DATADIR}/cli/dryrun/LinenoListener.py
*** Test Cases ***
Run Keyword With Keyword with Invalid Number of Arguments
- Check Test Case ${TESTNAME}
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Keyword Data ${tc[0]} BuiltIn.Run Keyword args=Log status=FAIL
+ Check Keyword Data ${tc[0, 0]} BuiltIn.Log args= status=FAIL
Run Keyword With Missing Keyword
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Keywords with variable in name are ignored
- Test Should Have Correct Keywords kw_index=0
- Test Should Have Correct Keywords BuiltIn.No Operation kw_index=1
- Test Should Have Correct Keywords kw_index=2
- Test Should Have Correct Keywords BuiltIn.No Operation kw_index=3
+ Test Should Have Correct Keywords kw_index=0
+ Test Should Have Correct Keywords BuiltIn.No Operation kw_index=1
+ Test Should Have Correct Keywords kw_index=2
+ Test Should Have Correct Keywords BuiltIn.No Operation kw_index=3
Keywords with variable in name are ignored also when variable is argument
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Run Keyword With UK
- Check Test Case ${TESTNAME}
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Keyword Data ${tc[0]} BuiltIn.Run Keyword If args=True, UK status=PASS
+ Check Keyword Data ${tc[0, 0]} UK status=PASS
+ Check Keyword Data ${tc[0, 0, 0]} BuiltIn.No Operation status=NOT RUN
Run Keyword With Failing UK
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Comment
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Set Test/Suite/Global Variable
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Variable Should (Not) Exist
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Get Variable Value
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Set Variable If
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Run Keywords When All Keywords Pass
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Run Keywords When One Keyword Fails
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Run Keywords When Multiple Keyword Fails
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Run Keywords With Arguments When All Keywords Pass
- Test Should Have Correct Keywords BuiltIn.Log Many BuiltIn.No Operation
+ Test Should Have Correct Keywords BuiltIn.Log Many BuiltIn.No Operation
Run Keywords With Arguments When One Keyword Fails
- Test Should Have Correct Keywords BuiltIn.Log BuiltIn.Log
+ Test Should Have Correct Keywords BuiltIn.Log BuiltIn.Log
Run Keywords With Arguments When Multiple Keyword Fails
- Test Should Have Correct Keywords BuiltIn.Log Unknown Keyword
+ Test Should Have Correct Keywords BuiltIn.Log Unknown Keyword
Run Keywords With Arguments With Variables
- Test Should Have Correct Keywords BuiltIn.Log
+ Test Should Have Correct Keywords BuiltIn.Log
Run Keyword in For Loop Pass
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Run Keyword in For Loop Fail
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Wait Until Keyword Succeeds Pass
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Wait Until Keyword Succeeds Fail
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Run Keyword If Pass
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Run Keyword If Fail
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Run Keyword If with ELSE
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Run Keyword If with ELSE IF
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Run Keyword If with ELSE IF and ELSE
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Run Keyword If with ELSE IF and ELSE without keywords
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Run Keyword If with escaped or non-caps ELSE IF and ELSE
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Run Keyword If with list variable in ELSE IF and ELSE
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Test Teardown Related Run Keyword Variants
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Given/When/Then
- ${tc} = Check Test Case ${TESTNAME}
- Length Should Be ${tc.kws[0].kws} 1
- Length Should Be ${tc.kws[1].kws} 3
- Length Should Be ${tc.kws[2].kws} 2
- Length Should Be ${tc.kws[3].kws} 3
- Length Should Be ${tc.kws[4].kws} 3
+ ${tc} = Check Test Case ${TESTNAME}
+ Length Should Be ${tc[0].body} 1
+ Length Should Be ${tc[1].body} 3
+ Length Should Be ${tc[2].body} 2
+ Length Should Be ${tc[3].body} 3
+ Length Should Be ${tc[4].body} 3
+
+Line number
+ Should Be Empty ${ERRORS}
+ ${tc} = Check Test Case Run Keyword With Missing Keyword
+ Should Be Equal ${tc[0].doc} Keyword 'Run Keyword' on line 14.
+ Should Be Equal ${tc[0, 0].doc} Keyword 'Missing' on line 14.
+ ${tc} = Check Test Case Run Keywords When One Keyword Fails
+ Should Be Equal ${tc[0].doc} Keyword 'Run Keywords' on line 68.
+ Should Be Equal ${tc[0, 0].doc} Keyword 'Fail' on line 68.
+ Should Be Equal ${tc[0, 2].doc} Keyword 'Log' on line 68.
+ Should Be Equal ${tc[0, 3].doc} Keyword 'UK' on line 68.
+ ${tc} = Check Test Case Run Keyword If Pass
+ Should Be Equal ${tc[0].doc} Keyword 'Run Keyword If' on line 114.
+ Should Be Equal ${tc[0, 0].doc} Keyword 'No Operation' on line 114.
diff --git a/atest/robot/cli/dryrun/try_except.robot b/atest/robot/cli/dryrun/try_except.robot
index d2c1d31db95..be64c9590eb 100644
--- a/atest/robot/cli/dryrun/try_except.robot
+++ b/atest/robot/cli/dryrun/try_except.robot
@@ -6,11 +6,11 @@ Resource dryrun_resource.robot
*** Test Cases ***
TRY
${tc} = Check Test Case ${TESTNAME}
- Check TRY Data ${tc.body[0].body[0]}
- Check Keyword Data ${tc.body[0].body[0].body[0]} resource.Simple UK
- Check Keyword Data ${tc.body[0].body[0].body[0].body[0]} BuiltIn.Log args=Hello from UK status=NOT RUN
- Check Keyword Data ${tc.body[0].body[1].body[0]} BuiltIn.Log args=handling it status=NOT RUN
- Check Keyword Data ${tc.body[0].body[2].body[0]} BuiltIn.Log args=in the else status=NOT RUN
- Check Keyword Data ${tc.body[0].body[3].body[0]} BuiltIn.Log args=in the finally status=NOT RUN
- Check TRY Data ${tc.body[1].body[0]} status=FAIL
- Check Keyword Data ${tc.body[1].body[0].body[0]} resource.Anarchy in the UK status=FAIL args=1, 2
+ Check TRY Data ${tc[0, 0]}
+ Check Keyword Data ${tc[0, 0, 0]} resource.Simple UK
+ Check Keyword Data ${tc[0, 0, 0, 0]} BuiltIn.Log args=Hello from UK status=NOT RUN
+ Check Keyword Data ${tc[0, 1, 0]} BuiltIn.Log args=handling it status=NOT RUN
+ Check Keyword Data ${tc[0, 2, 0]} BuiltIn.Log args=in the else status=NOT RUN
+ Check Keyword Data ${tc[0, 3, 0]} BuiltIn.Log args=in the finally status=NOT RUN
+ Check TRY Data ${tc[1, 0]} status=FAIL
+ Check Keyword Data ${tc[1, 0, 0]} resource.Anarchy in the UK status=FAIL args=1, 2
diff --git a/atest/robot/cli/dryrun/type_conversion.robot b/atest/robot/cli/dryrun/type_conversion.robot
index 3d5b9b0f2b5..3ed4f230cf4 100644
--- a/atest/robot/cli/dryrun/type_conversion.robot
+++ b/atest/robot/cli/dryrun/type_conversion.robot
@@ -3,7 +3,9 @@ Resource atest_resource.robot
*** Test Cases ***
Annotations
- Run Tests --dryrun keywords/type_conversion/annotations.robot
+ # Exclude test requiring Python 3.14 unconditionally to avoid a failure with
+ # older versions. It can be included once Python 3.14 is our minimum versoin.
+ Run Tests --dryrun --exclude require-py3.14 keywords/type_conversion/annotations.robot
Should be equal ${SUITE.status} PASS
Keyword Decorator
diff --git a/atest/robot/cli/dryrun/while.robot b/atest/robot/cli/dryrun/while.robot
index 3ed2f40d78c..65a1bda2f29 100644
--- a/atest/robot/cli/dryrun/while.robot
+++ b/atest/robot/cli/dryrun/while.robot
@@ -6,16 +6,16 @@ Resource dryrun_resource.robot
*** Test Cases ***
WHILE
${tc} = Check Test Case ${TESTNAME}
- Length should be ${tc.body[1].body} 1
- Length should be ${tc.body[1].body[0].body} 3
- Length should be ${tc.body[2].body} 1
- Length should be ${tc.body[1].body[0].body} 3
- Length should be ${tc.body[3].body} 3
- Length should be ${tc.body[3].body[0].body} 0
- Length should be ${tc.body[3].body[1].body} 1
- Length should be ${tc.body[3].body[2].body} 0
+ Length should be ${tc[1].body} 1
+ Length should be ${tc[1, 0].body} 3
+ Length should be ${tc[2].body} 1
+ Length should be ${tc[1, 0].body} 3
+ Length should be ${tc[3].body} 3
+ Length should be ${tc[3, 0].body} 0
+ Length should be ${tc[3, 1].body} 1
+ Length should be ${tc[3, 2].body} 0
WHILE with BREAK and CONTINUE
${tc} = Check Test Case ${TESTNAME}
- Length should be ${tc.body[1].body} 1
- Length should be ${tc.body[2].body} 1
+ Length should be ${tc[1].body} 1
+ Length should be ${tc[2].body} 1
diff --git a/atest/robot/cli/model_modifiers/ModelModifier.py b/atest/robot/cli/model_modifiers/ModelModifier.py
index 17feec7b982..f285434fa05 100644
--- a/atest/robot/cli/model_modifiers/ModelModifier.py
+++ b/atest/robot/cli/model_modifiers/ModelModifier.py
@@ -1,62 +1,81 @@
from robot.model import SuiteVisitor
+from robot.running import TestCase as RunningTestCase
+from robot.running.model import Argument
class ModelModifier(SuiteVisitor):
def __init__(self, *tags, **extra):
if extra:
- tags += tuple('%s-%s' % item for item in extra.items())
- self.config = tags or ('visited',)
+ tags += tuple("-".join(item) for item in extra.items())
+ self.config = tags or ("visited",)
def start_suite(self, suite):
config = self.config
- if config[0] == 'FAIL':
- raise RuntimeError(' '.join(self.config[1:]))
- elif config[0] == 'CREATE':
- tc = suite.tests.create(**dict(conf.split('-', 1) for conf in config[1:]))
- tc.body.create_keyword('Log', args=['Args as strings', 'level=INFO'])
- tc.body.create_keyword('Log', args=[('Args as tuples',), ('level', 'INFO')])
- tc.body.create_keyword('Log', args=[('Args as pos and named',), {'level': 'INFO'}])
+ if config[0] == "FAIL":
+ raise RuntimeError(" ".join(self.config[1:]))
+ elif config[0] == "CREATE":
+ tc = suite.tests.create(**dict(conf.split("-", 1) for conf in config[1:]))
+ tc.body.create_keyword("Log", args=["Hello", "level=INFO"])
+ if isinstance(tc, RunningTestCase):
+ # robot.running.model.Argument is a private/temporary API for creating
+ # named arguments with non-string values programmatically. It was added
+ # in RF 7.0.1 (#5031) after a failed attempt to add an API for this
+ # purpose in RF 7.0 (#5000).
+ tc.body.create_keyword(
+ "Log",
+ args=[Argument(None, "Argument object"), Argument("level", "INFO")],
+ )
+ tc.body.create_keyword(
+ "Should Contain",
+ args=[(1, 2, 3), Argument("item", 2)],
+ )
+ # Passing named args separately is supported since RF 7.1 (#5143).
+ tc.body.create_keyword(
+ "Log",
+ args=["Named args separately"],
+ named_args={"html": True, "level": '${{"INFO"}}'},
+ )
self.config = []
- elif config == ('REMOVE', 'ALL', 'TESTS'):
+ elif config == ("REMOVE", "ALL", "TESTS"):
suite.tests = []
else:
- suite.tests = [t for t in suite.tests if not t.tags.match('fail')]
+ suite.tests = [t for t in suite.tests if not t.tags.match("fail")]
def start_test(self, test):
- self.make_non_empty(test, 'Test')
- if hasattr(test.parent, 'resource'):
+ self.make_non_empty(test, "Test")
+ if hasattr(test.parent, "resource"):
for kw in test.parent.resource.keywords:
- self.make_non_empty(kw, 'Keyword')
+ self.make_non_empty(kw, "Keyword")
test.tags.add(self.config)
def make_non_empty(self, item, kind):
if not item.name:
- item.name = f'{kind} name made non-empty by modifier'
+ item.name = f"{kind} name made non-empty by modifier"
item.body.clear()
if not item.body:
- item.body.create_keyword('Log', [f'{kind} body made non-empty by modifier'])
+ item.body.create_keyword("Log", [f"{kind} body made non-empty by modifier"])
def start_for(self, for_):
- if for_.parent.name == 'FOR IN RANGE':
- for_.flavor = 'IN'
- for_.values = ['FOR', 'is', 'modified!']
+ if for_.parent.name == "FOR IN RANGE":
+ for_.flavor = "IN"
+ for_.values = ["FOR", "is", "modified!"]
def start_for_iteration(self, iteration):
for name, value in iteration.assign.items():
- iteration.assign[name] = value + ' (modified)'
- iteration.assign['${x}'] = 'new'
+ iteration.assign[name] = value + " (modified)"
+ iteration.assign["${x}"] = "new"
def start_if_branch(self, branch):
if branch.condition == "'${x}' == 'wrong'":
- branch.condition = 'True'
+ branch.condition = "True"
# With Robot
- if not hasattr(branch, 'status'):
- branch.body[0].config(name='Log', args=['going here!'])
+ if not hasattr(branch, "status"):
+ branch.body[0].config(name="Log", args=["going here!"])
# With Rebot
- elif branch.status == 'NOT RUN':
- branch.status = 'PASS'
- branch.condition = 'modified'
- branch.body[0].args = ['got here!']
- if branch.condition == '${i} == 9':
- branch.condition = 'False'
+ elif branch.status == "NOT RUN":
+ branch.status = "PASS"
+ branch.condition = "modified"
+ branch.body[0].args = ["got here!"]
+ if branch.condition == "${i} == 9":
+ branch.condition = "False"
diff --git a/atest/robot/cli/model_modifiers/pre_rebot.robot b/atest/robot/cli/model_modifiers/pre_rebot.robot
index 3490733e13f..a648d50c498 100644
--- a/atest/robot/cli/model_modifiers/pre_rebot.robot
+++ b/atest/robot/cli/model_modifiers/pre_rebot.robot
@@ -60,25 +60,25 @@ Modifiers are used before normal configuration
Modify FOR
[Setup] Modify FOR and IF
${tc} = Check Test Case FOR IN RANGE
- Should Be Equal ${tc.body[0].flavor} IN
- Should Be Equal ${tc.body[0].values} ${{('FOR', 'is', 'modified!')}}
- Should Be Equal ${tc.body[0].body[0].assign['\${i}']} 0 (modified)
- Should Be Equal ${tc.body[0].body[0].assign['\${x}']} new
- Check Log Message ${tc.body[0].body[0].body[0].msgs[0]} 0
- Should Be Equal ${tc.body[0].body[1].assign['\${i}']} 1 (modified)
- Should Be Equal ${tc.body[0].body[1].assign['\${x}']} new
- Check Log Message ${tc.body[0].body[1].body[0].msgs[0]} 1
- Should Be Equal ${tc.body[0].body[2].assign['\${i}']} 2 (modified)
- Should Be Equal ${tc.body[0].body[2].assign['\${x}']} new
- Check Log Message ${tc.body[0].body[2].body[0].msgs[0]} 2
+ Should Be Equal ${tc[0].flavor} IN
+ Should Be Equal ${tc[0].values} ${{('FOR', 'is', 'modified!')}}
+ Should Be Equal ${tc[0, 0].assign['\${i}']} 0 (modified)
+ Should Be Equal ${tc[0, 0].assign['\${x}']} new
+ Check Log Message ${tc[0, 0, 0, 0]} 0
+ Should Be Equal ${tc[0, 1].assign['\${i}']} 1 (modified)
+ Should Be Equal ${tc[0, 1].assign['\${x}']} new
+ Check Log Message ${tc[0, 1, 0, 0]} 1
+ Should Be Equal ${tc[0, 2].assign['\${i}']} 2 (modified)
+ Should Be Equal ${tc[0, 2].assign['\${x}']} new
+ Check Log Message ${tc[0, 2, 0, 0]} 2
Modify IF
[Setup] Should Be Equal ${PREV TEST NAME} Modify FOR
${tc} = Check Test Case If structure
- Should Be Equal ${tc.body[1].body[0].condition} modified
- Should Be Equal ${tc.body[1].body[0].status} PASS
- Should Be Equal ${tc.body[1].body[0].body[0].args[0]} got here!
- Should Be Equal ${tc.body[1].body[1].status} PASS
+ Should Be Equal ${tc[1, 0].condition} modified
+ Should Be Equal ${tc[1, 0].status} PASS
+ Should Be Equal ${tc[1, 0, 0].args[0]} got here!
+ Should Be Equal ${tc[1, 1].status} PASS
*** Keywords ***
Modify FOR and IF
diff --git a/atest/robot/cli/model_modifiers/pre_run.robot b/atest/robot/cli/model_modifiers/pre_run.robot
index e502fd9bd04..f52626345f7 100644
--- a/atest/robot/cli/model_modifiers/pre_run.robot
+++ b/atest/robot/cli/model_modifiers/pre_run.robot
@@ -47,9 +47,9 @@ Error if all tests removed
Modifier can fix empty test and keyword
Run Tests --RunEmptySuite --PreRun ${CURDIR}/ModelModifier.py core/empty_testcase_and_uk.robot
${tc} = Check Test Case Empty Test Case PASS ${EMPTY}
- Check Log Message ${tc.body[0].msgs[0]} Test body made non-empty by modifier
+ Check Log Message ${tc[0, 0]} Test body made non-empty by modifier
${tc} = Check Test Case Empty User Keyword PASS ${EMPTY}
- Check Log Message ${tc.body[0].body[0].msgs[0]} Keyword body made non-empty by modifier
+ Check Log Message ${tc[0, 0, 0]} Keyword body made non-empty by modifier
Check Test Case Test name made non-empty by modifier PASS ${EMPTY}
Modifiers are used before normal configuration
@@ -57,16 +57,26 @@ Modifiers are used before normal configuration
Stderr Should Be Empty
Length Should Be ${SUITE.tests} 1
${tc} = Check Test Case Created
- Check Log Message ${tc.body[0].msgs[0]} Args as strings
- Check Log Message ${tc.body[1].msgs[0]} Args as tuples
- Check Log Message ${tc.body[2].msgs[0]} Args as pos and named
+ Check Log Message ${tc[0, 0]} Hello
+ Check Keyword Data ${tc[0]} BuiltIn.Log args=Hello, level=INFO
Lists should be equal ${tc.tags} ${{['added']}}
+Modifiers can use special Argument objects in arguments
+ ${tc} = Check Test Case Created
+ Check Log Message ${tc[1, 0]} Argument object
+ Check Keyword Data ${tc[1]} BuiltIn.Log args=Argument object, level=INFO
+ Check Keyword Data ${tc[2]} BuiltIn.Should Contain args=(1, 2, 3), item=2
+
+Modifiers can pass positional and named arguments separately
+ ${tc} = Check Test Case Created
+ Check Log Message ${tc[3, 0]} Named args separately html=True
+ Check Keyword Data ${tc[3]} BuiltIn.Log args=Named args separately, html=True, level=\${{"INFO"}}
+
Modify FOR and IF
Run Tests --prerun ${CURDIR}/ModelModifier.py misc/for_loops.robot misc/if_else.robot
${tc} = Check Test Case FOR IN RANGE
- Check Log Message ${tc.body[0].body[0].body[0].msgs[0]} FOR
- Check Log Message ${tc.body[0].body[1].body[0].msgs[0]} is
- Check Log Message ${tc.body[0].body[2].body[0].msgs[0]} modified!
+ Check Log Message ${tc[0, 0, 0, 0]} FOR
+ Check Log Message ${tc[0, 1, 0, 0]} is
+ Check Log Message ${tc[0, 2, 0, 0]} modified!
${tc} = Check Test Case If structure
- Check Log Message ${tc.body[1].body[0].body[0].msgs[0]} going here!
+ Check Log Message ${tc[1, 0, 0, 0]} going here!
diff --git a/atest/robot/cli/rebot/invalid_usage.robot b/atest/robot/cli/rebot/invalid_usage.robot
index c149bef3fc4..57b5a0acfb1 100644
--- a/atest/robot/cli/rebot/invalid_usage.robot
+++ b/atest/robot/cli/rebot/invalid_usage.robot
@@ -20,6 +20,10 @@ Non-Existing Input
Existing And Non-Existing Input
Reading XML source '.*nönéx.xml' failed: .* source=${INPUTFILE} nönéx.xml nonex2.xml
+No tests in output
+ [Setup] Create File %{TEMPDIR}/no_tests.xml
+ Suite 'No Tests!' contains no tests. source=%{TEMPDIR}/no_tests.xml
+
Non-XML Input
[Setup] Create File %{TEMPDIR}/invalid.robot Hello, world
(\\[Fatal Error\\] .*: Content is not allowed in prolog.\\n)?Reading XML source '.*invalid.robot' failed: .*
@@ -62,7 +66,7 @@ Invalid --RemoveKeywords
*** Keywords ***
Rebot Should Fail
[Arguments] ${error} ${options}= ${source}=${INPUT}
- ${result} = Run Rebot ${options} ${source} default options= output=
- Should Be Equal As Integers ${result.rc} 252
+ ${result} = Run Rebot ${options} ${source} default options= output=None
+ Should Be Equal ${result.rc} 252 type=int
Should Be Empty ${result.stdout}
Should Match Regexp ${result.stderr} ^\\[ .*ERROR.* \\] ${error}${USAGETIP}$
diff --git a/atest/robot/cli/rebot/log_level.robot b/atest/robot/cli/rebot/log_level.robot
index 89f7de709c5..4282351e68d 100644
--- a/atest/robot/cli/rebot/log_level.robot
+++ b/atest/robot/cli/rebot/log_level.robot
@@ -7,29 +7,29 @@ ${LOG NAME} logfile.html
*** Test Cases ***
By default all messages are included
${tc} = Rebot
- Check Log Message ${tc.kws[0].msgs[0]} Arguments: [ 'Test 1' ] TRACE
- Check Log Message ${tc.kws[0].msgs[1]} Test 1 INFO
- Check Log Message ${tc.kws[0].msgs[2]} Return: None TRACE
- Check Log Message ${tc.kws[1].msgs[0]} Arguments: [ 'Logging with debug level' | 'DEBUG' ] TRACE
- Check Log Message ${tc.kws[1].msgs[1]} Logging with debug level DEBUG
- Check Log Message ${tc.kws[1].msgs[2]} Return: None TRACE
+ Check Log Message ${tc[0, 0]} Arguments: [ 'Test 1' ] TRACE
+ Check Log Message ${tc[0, 1]} Test 1 INFO
+ Check Log Message ${tc[0, 2]} Return: None TRACE
+ Check Log Message ${tc[1, 0]} Arguments: [ 'Logging with debug level' | 'DEBUG' ] TRACE
+ Check Log Message ${tc[1, 1]} Logging with debug level DEBUG
+ Check Log Message ${tc[1, 2]} Return: None TRACE
Min level should be 'TRACE' and default 'TRACE'
Levels below given level are ignored
${tc} = Rebot --loglevel debug
- Check Log Message ${tc.kws[0].msgs[0]} Test 1 INFO
- Check Log Message ${tc.kws[1].msgs[0]} Logging with debug level DEBUG
+ Check Log Message ${tc[0, 0]} Test 1 INFO
+ Check Log Message ${tc[1, 0]} Logging with debug level DEBUG
Min level should be 'DEBUG' and default 'DEBUG'
${tc} = Rebot -L INFO
- Check Log Message ${tc.kws[0].msgs[0]} Test 1 INFO
- Should Be Empty ${tc.kws[1].msgs}
- Should Be Empty ${tc.kws[2].kws[0].msgs}
+ Check Log Message ${tc[0, 0]} Test 1 INFO
+ Should Be Empty ${tc[1].body}
+ Should Be Empty ${tc[2, 0].body}
Min level should be 'INFO' and default 'INFO'
All messages are ignored when NONE level is used
${tc} = Rebot --loglevel NONE
- Should Be Empty ${tc.kws[0].msgs}
- Should Be Empty ${tc.kws[1].msgs}
+ Should Be Empty ${tc[0].body}
+ Should Be Empty ${tc[1].body}
Min level should be 'NONE' and default 'NONE'
Configure visible log level
diff --git a/atest/robot/cli/rebot/rebot_cli_resource.robot b/atest/robot/cli/rebot/rebot_cli_resource.robot
index 5dd39858680..fb96e02d13f 100644
--- a/atest/robot/cli/rebot/rebot_cli_resource.robot
+++ b/atest/robot/cli/rebot/rebot_cli_resource.robot
@@ -18,7 +18,7 @@ Run tests to create input file for Rebot
Run rebot and return outputs
[Arguments] ${options}
Create Output Directory
- ${result} = Run Rebot --outputdir ${CLI OUTDIR} ${options} ${INPUT FILE} default options= output=
+ ${result} = Run Rebot --outputdir ${CLI OUTDIR} ${options} ${INPUT FILE} default options= output=None
Should Be Equal ${result.rc} ${0}
@{outputs} = List Directory ${CLI OUTDIR}
RETURN @{outputs}
diff --git a/atest/robot/cli/rebot/remove_keywords/all_passed_tag_and_name.robot b/atest/robot/cli/rebot/remove_keywords/all_passed_tag_and_name.robot
index b456598f5ab..9c8de17fbaa 100644
--- a/atest/robot/cli/rebot/remove_keywords/all_passed_tag_and_name.robot
+++ b/atest/robot/cli/rebot/remove_keywords/all_passed_tag_and_name.robot
@@ -6,185 +6,193 @@ Resource remove_keywords_resource.robot
*** Test Cases ***
All Mode
[Setup] Run Rebot and set My Suite --RemoveKeywords ALL 0
- Keyword Should Be Empty ${MY SUITE.setup} My Keyword Suite Setup
- Keyword Should Contain Removal Message ${MY SUITE.setup}
${tc1} = Check Test Case Pass
${tc2} = Check Test Case Fail
- Length Should Be ${tc1.body} 1
- Keyword Should Be Empty ${tc1.body[0]} My Keyword Pass
- Length Should Be ${tc2.body} 2
- Keyword Should Be Empty ${tc2.body[0]} My Keyword Fail
- Keyword Should Be Empty ${tc2.body[1]} BuiltIn.Fail Expected failure
- Keyword Should Contain Removal Message ${tc2.body[1]} Expected failure
${tc3} = Check Test Case Test with setup and teardown
- Keyword Should Be Empty ${tc3.setup} Test Setup
+ Keyword Should Be Empty ${MY SUITE.setup} My Keyword Suite Setup
+ Keyword Should Contain Removal Message ${MY SUITE.setup}
+ Length Should Be ${tc1.body} 3
+ Keyword Should Be Empty ${tc1[0]} My Keyword Pass
+ Length Should Be ${tc2.body} 2
+ Keyword Should Be Empty ${tc2[0]} My Keyword Fail
+ Keyword Should Be Empty ${tc2[1]} BuiltIn.Fail Expected failure
+ Keyword Should Contain Removal Message ${tc2[1]} Expected failure
+ Keyword Should Be Empty ${tc3.setup} Test Setup
Keyword Should Contain Removal Message ${tc3.setup}
- Keyword Should Be Empty ${tc3.teardown} Test Teardown
+ Keyword Should Be Empty ${tc3.teardown} Test Teardown
Keyword Should Contain Removal Message ${tc3.teardown}
Warnings Are Removed In All Mode
[Setup] Verify previous test and set My Suite All Mode 1
- Keyword Should Be Empty ${MY SUITE.setup} Warning in suite setup
- Keyword Should Be Empty ${MY SUITE.teardown} Warning in suite teardown
${tc1} ${tc2}= Set Variable ${MY SUITE.tests[:2]}
- Length Should Be ${tc1.body} 1
- Length Should Be ${tc2.body} 1
- Keyword Should Be Empty ${tc1.body[0]} Warning in test case
- Keyword Should Be Empty ${tc2.body[0]} No warning
+ Keyword Should Be Empty ${MY SUITE.setup} Warning in suite setup
+ Keyword Should Be Empty ${MY SUITE.teardown} Warning in suite teardown
+ Length Should Be ${tc1.body} 1
+ Keyword Should Be Empty ${tc1[0]} Warning in test case
+ Length Should Be ${tc2.body} 1
+ Keyword Should Be Empty ${tc2[0]} No warning
Logged Warnings Are Preserved In Execution Errors
Errors Are Removed In All Mode
${tc} = Check Test Case Error in test case
- Keyword Should Be Empty ${tc.body[0]} Error in test case
+ Keyword Should Be Empty ${tc[0]} Error in test case
Logged Errors Are Preserved In Execution Errors
IF/ELSE in All mode
${tc} = Check Test Case IF structure
- Length Should Be ${tc.body} 2
- Length Should Be ${tc.body[1].body} 3
- IF Branch Should Be Empty ${tc.body[1].body[0]} IF '\${x}' == 'wrong'
- IF Branch Should Be Empty ${tc.body[1].body[1]} ELSE IF '\${x}' == 'value'
- IF Branch Should Be Empty ${tc.body[1].body[2]} ELSE
+ Length Should Be ${tc.body} 2
+ Length Should Be ${tc[1].body} 3
+ IF Branch Should Be Empty ${tc[1, 0]} IF '\${x}' == 'wrong'
+ IF Branch Should Be Empty ${tc[1, 1]} ELSE IF '\${x}' == 'value'
+ IF Branch Should Be Empty ${tc[1, 2]} ELSE
FOR in All mode
- ${tc} = Check Test Case FOR
- Length Should Be ${tc.body} 1
- FOR Loop Should Be Empty ${tc.body[0]} IN
- ${tc} = Check Test Case FOR IN RANGE
- Length Should Be ${tc.body} 1
- FOR Loop Should Be Empty ${tc.body[0]} IN RANGE
+ ${tc1} = Check Test Case FOR
+ ${tc2} = Check Test Case FOR IN RANGE
+ Length Should Be ${tc1.body} 1
+ FOR Loop Should Be Empty ${tc1[0]} IN
+ Length Should Be ${tc2.body} 1
+ FOR Loop Should Be Empty ${tc2[0]} IN RANGE
TRY/EXCEPT in All mode
${tc} = Check Test Case Everything
- Length Should Be ${tc.body} 1
- Length Should Be ${tc.body[0].body} 5
- TRY Branch Should Be Empty ${tc.body[0].body[0]} TRY Ooops!
- TRY Branch Should Be Empty ${tc.body[0].body[1]} EXCEPT
- TRY Branch Should Be Empty ${tc.body[0].body[2]} EXCEPT
- TRY Branch Should Be Empty ${tc.body[0].body[3]} ELSE
- TRY Branch Should Be Empty ${tc.body[0].body[4]} FINALLY
+ Length Should Be ${tc.body} 1
+ Length Should Be ${tc[0].body} 5
+ TRY Branch Should Be Empty ${tc[0, 0]} TRY Ooops!
+ TRY Branch Should Be Empty ${tc[0, 1]} EXCEPT
+ TRY Branch Should Be Empty ${tc[0, 2]} EXCEPT
+ TRY Branch Should Be Empty ${tc[0, 3]} ELSE
+ TRY Branch Should Be Empty ${tc[0, 4]} FINALLY
WHILE and VAR in All mode
${tc} = Check Test Case WHILE loop executed multiple times
- Length Should Be ${tc.body} 2
- Should Be Equal ${tc.body[1].type} WHILE
- Should Be Empty ${tc.body[1].body}
- Should Be Equal ${tc.body[1].message} *HTML* ${DATA REMOVED}
+ Length Should Be ${tc.body} 2
+ Should Be Equal ${tc[1].type} WHILE
+ Should Be Empty ${tc[1].body}
+ Should Be Equal ${tc[1].message} *HTML* ${DATA REMOVED}
VAR in All mode
- ${tc} = Check Test Case IF structure
- Should Be Equal ${tc.body[0].type} VAR
- Should Be Empty ${tc.body[0].body}
- Should Be Equal ${tc.body[0].message} ${EMPTY}
- ${tc} = Check Test Case WHILE loop executed multiple times
- Should Be Equal ${tc.body[0].type} VAR
- Should Be Empty ${tc.body[0].body}
- Should Be Equal ${tc.body[0].message} ${EMPTY}
+ ${tc1} = Check Test Case IF structure
+ ${tc2} = Check Test Case WHILE loop executed multiple times
+ Should Be Equal ${tc1[0].type} VAR
+ Should Be Empty ${tc1[0].body}
+ Should Be Equal ${tc1[0].message} *HTML* ${DATA REMOVED}
+ Should Be Equal ${tc2[0].type} VAR
+ Should Be Empty ${tc2[0].body}
+ Should Be Equal ${tc2[0].message} *HTML* ${DATA REMOVED}
Passed Mode
[Setup] Run Rebot and set My Suite --removekeywords passed 0
- Keyword Should Not Be Empty ${MY SUITE.setup} My Keyword Suite Setup
${tc1} = Check Test Case Pass
${tc2} = Check Test Case Fail
- Length Should Be ${tc1.body} 1
- Keyword Should Be Empty ${tc1.body[0]} My Keyword Pass
- Keyword Should Contain Removal Message ${tc1.body[0]}
- Length Should Be ${tc2.body} 2
- Keyword Should Not Be Empty ${tc2.body[0]} My Keyword Fail
- Keyword Should Not Be Empty ${tc2.body[1]} BuiltIn.Fail Expected failure
${tc3} = Check Test Case Test with setup and teardown
- Keyword Should Be Empty ${tc3.setup} Test Setup
- Keyword Should Contain Removal Message ${tc3.setup}
- Keyword Should Be Empty ${tc3.teardown} Test Teardown
- Keyword Should Contain Removal Message ${tc3.teardown}
+ Keyword Should Not Be Empty ${MY SUITE.setup} My Keyword Suite Setup
+ Length Should Be ${tc1.body} 3
+ Keyword Should Be Empty ${tc1[0]} My Keyword Pass
+ Keyword Should Contain Removal Message ${tc1[0]}
+ Length Should Be ${tc2.body} 4
+ Check Log message ${tc2[0]} Hello 'Fail', says listener!
+ Keyword Should Not Be Empty ${tc2[1]} My Keyword Fail
+ Keyword Should Not Be Empty ${tc2[2]} BuiltIn.Fail Expected failure
+ Check Log message ${tc2[3]} Bye 'Fail', says listener!
+ Keyword Should Be Empty ${tc3.setup} Test Setup
+ Keyword Should Contain Removal Message ${tc3.setup}
+ Keyword Should Be Empty ${tc3.teardown} Test Teardown
+ Keyword Should Contain Removal Message ${tc3.teardown}
Warnings Are Not Removed In Passed Mode
[Setup] Verify previous test and set My Suite Passed Mode 1
- Keyword Should Not Be Empty ${MY SUITE.setup} Warning in suite setup
- Keyword Should Not Be Empty ${MY SUITE.teardown} Warning in suite teardown
${tc1} ${tc2}= Set Variable ${MY SUITE.tests[:2]}
- Length Should Be ${tc1.body} 1
- Keyword Should Not Be Empty ${tc1.body[0]} Warning in test case
- Keyword Should Not Be Empty ${tc1.body[0].body[0].body[0].body[0]} BuiltIn.Log Warning in \${where} WARN
- Length Should Be ${tc2.body} 1
- Keyword Should Be Empty ${tc2.body[0]} No warning
+ Keyword Should Not Be Empty ${MY SUITE.setup} Warning in suite setup
+ Keyword Should Not Be Empty ${MY SUITE.teardown} Warning in suite teardown
+ Length Should Be ${tc1.body} 3
+ Check Log message ${tc1[0]} Hello 'Warning in test case', says listener!
+ Keyword Should Not Be Empty ${tc1[1]} Warning in test case
+ Check Log message ${tc1[2]} Bye 'Warning in test case', says listener!
+ Keyword Should Not Be Empty ${tc1[1, 0, 0, 0]} BuiltIn.Log Warning in \${where} WARN
+ Length Should Be ${tc2.body} 1
+ Keyword Should Be Empty ${tc2[0]} No warning
Logged Warnings Are Preserved In Execution Errors
Errors Are Not Removed In Passed Mode
[Setup] Previous test should have passed Warnings Are Not Removed In Passed Mode
${tc} = Check Test Case Error in test case
- Check Log Message ${tc.body[0].body[0].msgs[0]} Logged errors supported since 2.9 ERROR
+ Length Should Be ${tc.body} 3
+ Check Log message ${tc[0]} Hello 'Error in test case', says listener!
+ Check Log Message ${tc[1, 0, 0]} Logged errors supported since 2.9 ERROR
+ Check Log message ${tc[2]} Bye 'Error in test case', says listener!
Logged Errors Are Preserved In Execution Errors
Name Mode
[Setup] Run Rebot and set My Suite
... --removekeywords name:BuiltIn.Fail --RemoveK NAME:??_KEYWORD --RemoveK NaMe:*WARN*IN* --removek name:errorin* 0
- Keyword Should Be Empty ${MY SUITE.setup} My Keyword Suite Setup
- Keyword Should Contain Removal Message ${MY SUITE.setup}
${tc1} = Check Test Case Pass
${tc2} = Check Test Case Fail
- Length Should Be ${tc1.body} 1
- Keyword Should Be Empty ${tc1.body[0]} My Keyword Pass
- Keyword Should Contain Removal Message ${tc1.body[0]}
- Length Should Be ${tc2.body} 2
- Keyword Should Be Empty ${tc2.body[0]} My Keyword Fail
- Keyword Should Contain Removal Message ${tc2.body[0]}
- Keyword Should Be Empty ${tc2.body[1]} BuiltIn.Fail Expected failure
- Keyword Should Contain Removal Message ${tc2.body[0]}
+ Keyword Should Be Empty ${MY SUITE.setup} My Keyword Suite Setup
+ Keyword Should Contain Removal Message ${MY SUITE.setup}
+ Length Should Be ${tc1.body} 5
+ Keyword Should Be Empty ${tc1[1]} My Keyword Pass
+ Keyword Should Contain Removal Message ${tc1[1]}
+ Length Should Be ${tc2.body} 4
+ Keyword Should Be Empty ${tc2[1]} My Keyword Fail
+ Keyword Should Contain Removal Message ${tc2[1]}
+ Keyword Should Be Empty ${tc2[2]} BuiltIn.Fail Expected failure
Warnings Are Not Removed In Name Mode
[Setup] Verify previous test and set My Suite Name Mode 1
- Keyword Should Not Be Empty ${MY SUITE.setup} Warning in suite setup
- Keyword Should Not Be Empty ${MY SUITE.teardown} Warning in suite teardown
${tc1} ${tc2}= Set Variable ${MY SUITE.tests[:2]}
- Length Should Be ${tc1.body} 1
- Length Should Be ${tc2.body} 1
- Keyword Should Not Be Empty ${tc1.body[0]} Warning in test case
- Keyword Should Not Be Empty ${tc1.body[0].body[0].body[0].body[0]} BuiltIn.Log Warning in \${where} WARN
- Keyword Should Be Empty ${tc2.body[0]} No warning
+ Keyword Should Not Be Empty ${MY SUITE.setup} Warning in suite setup
+ Keyword Should Not Be Empty ${MY SUITE.teardown} Warning in suite teardown
+ Length Should Be ${tc1.body} 3
+ Length Should Be ${tc2.body} 3
+ Keyword Should Not Be Empty ${tc1[1]} Warning in test case
+ Keyword Should Not Be Empty ${tc1[1, 0, 0, 0]} BuiltIn.Log Warning in \${where} WARN
+ Keyword Should Be Empty ${tc2[1]} No warning
Logged Warnings Are Preserved In Execution Errors
Errors Are Not Removed In Name Mode
[Setup] Previous test should have passed Warnings Are Not Removed In Name Mode
${tc} = Check Test Case Error in test case
- Check Log Message ${tc.body[0].body[0].msgs[0]} Logged errors supported since 2.9 ERROR
+ Check Log Message ${tc[1, 0, 0]} Logged errors supported since 2.9 ERROR
Logged Errors Are Preserved In Execution Errors
Tag Mode
[Setup] Run Rebot and set My Suite --removekeywords tag:force --RemoveK TAG:warn 0
- Keyword Should Be Empty ${MY SUITE.setup} My Keyword Suite Setup
- Keyword Should Contain Removal Message ${MY SUITE.setup}
${tc1} = Check Test Case Pass
${tc2} = Check Test Case Fail
- Length Should Be ${tc1.body} 1
- Keyword Should Be Empty ${tc1.body[0]} My Keyword Pass
- Keyword Should Contain Removal Message ${tc1.body[0]}
- Length Should Be ${tc2.body} 2
- Keyword Should Be Empty ${tc2.body[0]} My Keyword Fail
- Keyword Should Contain Removal Message ${tc2.body[0]}
- Keyword Should Not Be Empty ${tc2.body[1]} BuiltIn.Fail Expected failure
+ Keyword Should Be Empty ${MY SUITE.setup} My Keyword Suite Setup
+ Keyword Should Contain Removal Message ${MY SUITE.setup}
+ Length Should Be ${tc1.body} 5
+ Keyword Should Be Empty ${tc1[1]} My Keyword Pass
+ Keyword Should Contain Removal Message ${tc1[1]}
+ Length Should Be ${tc2.body} 4
+ Keyword Should Be Empty ${tc2[1]} My Keyword Fail
+ Keyword Should Contain Removal Message ${tc2[1]}
+ Keyword Should Not Be Empty ${tc2[2]} BuiltIn.Fail Expected failure
Warnings Are Not Removed In Tag Mode
[Setup] Verify previous test and set My Suite Tag Mode 1
- Keyword Should Not Be Empty ${MY SUITE.setup} Warning in suite setup
- Keyword Should Not Be Empty ${MY SUITE.teardown} Warning in suite teardown
${tc1} ${tc2}= Set Variable ${MY SUITE.tests[:2]}
- Length Should Be ${tc1.body} 1
- Length Should Be ${tc2.body} 1
- Keyword Should Not Be Empty ${tc1.body[0]} Warning in test case
- Keyword Should Not Be Empty ${tc1.body[0].body[0].body[0].body[0]} BuiltIn.Log Warning in \${where} WARN
- Keyword Should Be Empty ${tc2.body[0]} No warning
+ Keyword Should Not Be Empty ${MY SUITE.setup} Warning in suite setup
+ Keyword Should Not Be Empty ${MY SUITE.teardown} Warning in suite teardown
+ Length Should Be ${tc1.body} 3
+ Keyword Should Not Be Empty ${tc1[1]} Warning in test case
+ Keyword Should Not Be Empty ${tc1[1, 0, 0, 0]} BuiltIn.Log Warning in \${where} WARN
+ Length Should Be ${tc2.body} 3
+ Keyword Should Be Empty ${tc2[1]} No warning
Logged Warnings Are Preserved In Execution Errors
Errors Are Not Removed In Tag Mode
[Setup] Previous test should have passed Warnings Are Not Removed In Tag Mode
${tc} = Check Test Case Error in test case
- Check Log Message ${tc.body[0].body[0].msgs[0]} Logged errors supported since 2.9 ERROR
+ Check Log Message ${tc[1, 0, 0]} Logged errors supported since 2.9 ERROR
Logged Errors Are Preserved In Execution Errors
*** Keywords ***
Run Some Tests
- ${suites} = Catenate
+ VAR ${options}
+ ... --listener AddMessagesToTestBody
+ VAR ${suites}
... misc/pass_and_fail.robot
... misc/warnings_and_errors.robot
... misc/if_else.robot
@@ -192,7 +200,7 @@ Run Some Tests
... misc/try_except.robot
... misc/while.robot
... misc/setups_and_teardowns.robot
- Create Output With Robot ${INPUTFILE} ${EMPTY} ${suites}
+ Create Output With Robot ${INPUTFILE} ${options} ${suites}
Run Rebot And Set My Suite
[Arguments] ${rebot params} ${suite index}
diff --git a/atest/robot/cli/rebot/remove_keywords/for_loop_keywords.robot b/atest/robot/cli/rebot/remove_keywords/for_loop_keywords.robot
index 0655004a9b1..05bc9148bd2 100644
--- a/atest/robot/cli/rebot/remove_keywords/for_loop_keywords.robot
+++ b/atest/robot/cli/rebot/remove_keywords/for_loop_keywords.robot
@@ -12,50 +12,50 @@ ${4 REMOVED} 4 passing items removed using the --r
*** Test Cases ***
Passed Steps Are Removed Except The Last One
${tc}= Check Test Case Simple loop
- Length Should Be ${tc.kws[1].kws} 1
- Should Be Equal ${tc.kws[1].message} *HTML* ${1 REMOVED}
- Should Be Equal ${tc.kws[1].kws[0].status} PASS
+ Length Should Be ${tc[1].body} 1
+ Should Be Equal ${tc[1].message} *HTML* ${1 REMOVED}
+ Should Be Equal ${tc[1, 0].status} PASS
Failed Steps Are Not Removed
${tc}= Check Test Case Failure inside FOR 2
- Length Should Be ${tc.body[0].body} 1
- Should Be Equal ${tc.body[0].message} *HTML* Failure with <4>${3 REMOVED}
- Should Be Equal ${tc.body[0].body[0].type} ITERATION
- Should Be Equal ${tc.body[0].body[0].assign['\${num}']} 4
- Should Be Equal ${tc.body[0].body[0].status} FAIL
- Length Should Be ${tc.body[0].body[0].body} 3
- Should Be Equal ${tc.body[0].body[0].body[-1].status} NOT RUN
+ Length Should Be ${tc[0].body} 1
+ Should Be Equal ${tc[0].message} *HTML* Failure with <4>${3 REMOVED}
+ Should Be Equal ${tc[0, 0].type} ITERATION
+ Should Be Equal ${tc[0, 0].assign['\${num}']} 4
+ Should Be Equal ${tc[0, 0].status} FAIL
+ Length Should Be ${tc[0, 0].body} 3
+ Should Be Equal ${tc[0, 0, -1].status} NOT RUN
Steps With Warning Are Not Removed
${tc}= Check Test Case Variables in values
- Length Should Be ${tc.kws[0].kws} 2
- Should Be Equal ${tc.kws[0].message} *HTML* ${4 REMOVED}
- Check Log Message ${tc.kws[0].kws[0].kws[-1].kws[0].msgs[0]} Presidential Candidate! WARN
- Check Log Message ${tc.kws[0].kws[1].kws[-1].kws[0].msgs[0]} Presidential Candidate! WARN
+ Length Should Be ${tc[0].body} 2
+ Should Be Equal ${tc[0].message} *HTML* ${4 REMOVED}
+ Check Log Message ${tc[0, 0, -1, 0, 0]} Presidential Candidate! WARN
+ Check Log Message ${tc[0, 1, -1, 0, 0]} Presidential Candidate! WARN
Steps From Nested Loops Are Removed
${tc}= Check Test Case Nested Loop Syntax
- Length Should Be ${tc.kws[0].kws} 1
- Should Be Equal ${tc.kws[0].message} *HTML* ${2 REMOVED}
- Length Should Be ${tc.kws[0].kws[0].kws[1].kws} 1
- Should Be Equal ${tc.kws[0].kws[0].kws[1].message} *HTML* ${2 REMOVED}
+ Length Should Be ${tc[0].body} 1
+ Should Be Equal ${tc[0].message} *HTML* ${2 REMOVED}
+ Length Should Be ${tc[0, 0, 1].body} 1
+ Should Be Equal ${tc[0, 0, 1].message} *HTML* ${2 REMOVED}
Steps From Loops In Keywords From Loops Are Removed
${tc}= Check Test Case Keyword with loop calling other keywords with loops
- Length Should Be ${tc.kws[0].kws[0].kws} 1
- Should Be Equal ${tc.kws[0].kws[0].message} This ought to be enough
- Length Should Be ${tc.kws[0].kws[0].kws[0].kws[0].kws[1].kws} 1
- Should Be Equal ${tc.kws[0].kws[0].kws[0].kws[0].kws[1].message} *HTML* ${1 REMOVED}
- Length Should Be ${tc.kws[0].kws[0].kws[0].kws[1].kws[0].kws} 1
- Should Be Equal ${tc.kws[0].kws[0].kws[0].kws[1].kws[0].message} *HTML* ${1 REMOVED}
+ Length Should Be ${tc[0, 0].body} 1
+ Should Be Equal ${tc[0, 0].message} This ought to be enough
+ Length Should Be ${tc[0, 0, 0, 0, 1].body} 1
+ Should Be Equal ${tc[0, 0, 0, 0, 1].message} *HTML* ${1 REMOVED}
+ Length Should Be ${tc[0, 0, 0, 1, 0].body} 1
+ Should Be Equal ${tc[0, 0, 0, 1, 0].message} *HTML* ${1 REMOVED}
Empty Loops Are Handled Correctly
${tc}= Check Test Case Empty body
- Should Be Equal ${tc.body[0].status} FAIL
- Should Be Equal ${tc.body[0].message} FOR loop cannot be empty.
- Should Be Equal ${tc.body[0].body[0].type} ITERATION
- Should Be Equal ${tc.body[0].body[0].status} NOT RUN
- Should Be Empty ${tc.body[0].body[0].body}
+ Should Be Equal ${tc[0].status} FAIL
+ Should Be Equal ${tc[0].message} FOR loop cannot be empty.
+ Should Be Equal ${tc[0, 0].type} ITERATION
+ Should Be Equal ${tc[0, 0].status} NOT RUN
+ Should Be Empty ${tc[0, 0].body}
*** Keywords ***
Remove For Loop Keywords With Rebot
diff --git a/atest/robot/cli/rebot/remove_keywords/remove_keywords_resource.robot b/atest/robot/cli/rebot/remove_keywords/remove_keywords_resource.robot
index 079041f328a..f11bebb58fe 100644
--- a/atest/robot/cli/rebot/remove_keywords/remove_keywords_resource.robot
+++ b/atest/robot/cli/rebot/remove_keywords/remove_keywords_resource.robot
@@ -35,9 +35,7 @@ TRY Branch Should Be Empty
Keyword Should Not Be Empty
[Arguments] ${kw} ${name} @{args}
Check Keyword Name And Args ${kw} ${name} @{args}
- ${num_keywords}= Get Length ${kw.kws}
- ${num_messages}= Get Length ${kw.messages}
- Should Be True ${num_keywords} + ${num_messages} > 0
+ Should Not Be Empty ${kw.body}
Check Keyword Name And Args
[Arguments] ${kw} ${name} @{args}
diff --git a/atest/robot/cli/rebot/remove_keywords/wait_until_keyword_succeeds.robot b/atest/robot/cli/rebot/remove_keywords/wait_until_keyword_succeeds.robot
index b9dbc4cddcb..8bdec7783e2 100644
--- a/atest/robot/cli/rebot/remove_keywords/wait_until_keyword_succeeds.robot
+++ b/atest/robot/cli/rebot/remove_keywords/wait_until_keyword_succeeds.robot
@@ -8,21 +8,22 @@ Last failing Step is not removed
${expected} = Catenate
... [*]HTML[*] Keyword 'Fail' failed after retrying for 50 milliseconds.
... The last error was: Not gonna happen? failing item* removed using the --remove-keywords option.
- Should Match ${tc.body[0].message} ${expected}
+ Should Match ${tc[0].message} ${expected}
Last passing Step is not removed
${tc}= Check Number Of Keywords Passes before timeout 2
- Should Be Equal ${tc.body[0].message} *HTML* 1 failing item removed using the --remove-keywords option.
+ Should Be Equal ${tc[0].message} *HTML* 1 failing item removed using the --remove-keywords option.
Steps containing warnings are not removed
${tc}= Check Number Of Keywords Warnings 3
- Should be Equal ${tc.body[0].message} ${EMPTY}
+ Should be Equal ${tc[0].message} ${EMPTY}
Check Number Of Keywords One Warning 2
Nested Wait Until keywords are removed
${tc}= Check Test Case Nested
- Length Should Be ${tc.body[0].body.filter(messages=False)} 1
- Length Should Be ${tc.body[0].body[0].body} 1
+ Length Should Be ${tc[0].messages} 1
+ Length Should Be ${tc[0].non_messages} 1
+ Length Should Be ${tc[0, 0].body} 1
*** Keywords ***
Remove Wait Until Keyword Succeeds with Rebot
@@ -32,6 +33,5 @@ Remove Wait Until Keyword Succeeds with Rebot
Check Number Of Keywords
[Arguments] ${name} ${expected}
${tc}= Check Test Case ${name}
- Length Should Be ${tc.body[0].body.filter(messages=False)} ${expected}
+ Length Should Be ${tc[0].non_messages} ${expected}
RETURN ${tc}
-
diff --git a/atest/robot/cli/rebot/remove_keywords/while_loop_keywords.robot b/atest/robot/cli/rebot/remove_keywords/while_loop_keywords.robot
index ba03bd55e92..ea36561aef8 100644
--- a/atest/robot/cli/rebot/remove_keywords/while_loop_keywords.robot
+++ b/atest/robot/cli/rebot/remove_keywords/while_loop_keywords.robot
@@ -10,24 +10,24 @@ ${4 REMOVED} 4 passing items removed using the --r
*** Test Cases ***
Passed Steps Are Removed Except The Last One
${tc}= Check Test Case Loop executed multiple times
- Length Should Be ${tc.kws[0].kws} 1
- Should Be Equal ${tc.kws[0].message} *HTML* ${4 REMOVED}
- Should Be Equal ${tc.kws[0].kws[0].status} PASS
+ Length Should Be ${tc[0].body} 1
+ Should Be Equal ${tc[0].message} *HTML* ${4 REMOVED}
+ Should Be Equal ${tc[0, 0].status} PASS
Failed Steps Are Not Removed
${tc}= Check Test Case Execution fails after some loops
- Length Should Be ${tc.kws[0].kws} 1
- Should Be Equal ${tc.kws[0].message} *HTML* Oh no, got 4${2 REMOVED}
- Should Be Equal ${tc.kws[0].kws[0].status} FAIL
- Length Should Be ${tc.kws[0].kws[0].kws} 3
- Should Be Equal ${tc.kws[0].kws[0].kws[-1].status} NOT RUN
+ Length Should Be ${tc[0].body} 1
+ Should Be Equal ${tc[0].message} *HTML* Oh no, got 4${2 REMOVED}
+ Should Be Equal ${tc[0, 0].status} FAIL
+ Length Should Be ${tc[0, 0].body} 3
+ Should Be Equal ${tc[0, 0, -1].status} NOT RUN
Steps From Nested Loops Are Removed
${tc}= Check Test Case Loop in loop
- Length Should Be ${tc.kws[0].kws} 1
- Should Be Equal ${tc.kws[0].message} *HTML* ${4 REMOVED}
- Length Should Be ${tc.kws[0].kws[0].kws[2].kws} 1
- Should Be Equal ${tc.kws[0].kws[0].kws[2].message} *HTML* ${2 REMOVED}
+ Length Should Be ${tc[0].body} 1
+ Should Be Equal ${tc[0].message} *HTML* ${4 REMOVED}
+ Length Should Be ${tc[0, 0, 2].body} 1
+ Should Be Equal ${tc[0, 0, 2].message} *HTML* ${2 REMOVED}
*** Keywords ***
Remove WHILE Keywords With Rebot
diff --git a/atest/robot/cli/runner/ROBOT_OPTIONS.robot b/atest/robot/cli/runner/ROBOT_OPTIONS.robot
index 731d3c5a052..60a569aec2b 100644
--- a/atest/robot/cli/runner/ROBOT_OPTIONS.robot
+++ b/atest/robot/cli/runner/ROBOT_OPTIONS.robot
@@ -8,10 +8,10 @@ Use defaults
Run Tests ${EMPTY} misc/pass_and_fail.robot
Should Be Equal ${SUITE.name} Default
${tc} = Check Test Tags Pass force pass default with spaces
- Should Be Equal ${tc.kws[0].kws[0].status} NOT RUN
+ Should Be Equal ${tc[0, 0].status} NOT RUN
Override defaults
Run Tests -N Given -G given --nodryrun misc/pass_and_fail.robot
Should Be Equal ${SUITE.name} Given
${tc} = Check Test Tags Pass force pass default with spaces given
- Should Be Equal ${tc.kws[0].kws[0].status} PASS
+ Should Be Equal ${tc[0, 0].status} PASS
diff --git a/atest/robot/cli/runner/cli_resource.robot b/atest/robot/cli/runner/cli_resource.robot
index fa485a3ce69..9d060098af3 100644
--- a/atest/robot/cli/runner/cli_resource.robot
+++ b/atest/robot/cli/runner/cli_resource.robot
@@ -24,7 +24,7 @@ Output Directory Should Be Empty
Run Some Tests
[Arguments] ${options}=-l none -r none
- ${result} = Run Tests -d ${CLI OUTDIR} ${options} ${TEST FILE} default options= output=
+ ${result} = Run Tests -d ${CLI OUTDIR} ${options} ${TEST FILE} default options= output=None
Should Be Equal ${result.rc} ${0}
RETURN ${result}
@@ -37,7 +37,7 @@ Tests Should Pass Without Errors
Run Should Fail
[Arguments] ${options} ${error} ${regexp}=False
- ${result} = Run Tests ${options} default options= output=
+ ${result} = Run Tests ${options} default options= output=None
Should Be Equal As Integers ${result.rc} 252
Should Be Empty ${result.stdout}
IF ${regexp}
diff --git a/atest/robot/cli/runner/debugfile.robot b/atest/robot/cli/runner/debugfile.robot
index 838f573858b..7f5018948df 100644
--- a/atest/robot/cli/runner/debugfile.robot
+++ b/atest/robot/cli/runner/debugfile.robot
@@ -26,6 +26,10 @@ Debugfile
Stdout Should Match Regexp .*Debug: {3}${path}.*
Syslog Should Match Regexp .*Debug: ${path}.*
+Debug file messages are not delayed when timeouts are active
+ Run Tests -b debug.txt cli/runner/debugfile.robot
+ Check Test Case ${TEST NAME}
+
Debugfile Log Level Should Always Be Debug
[Documentation] --loglevel option should not affect what's written to debugfile
Run Tests Without Processing Output --outputdir ${CLI OUTDIR} -b debug.txt -o o.xml --loglevel WARN ${TESTFILE}
@@ -45,8 +49,8 @@ Debugfile timestamps are accurate
${tc} = Check Test Case LibraryAddsTimestampAsInteger
${content} = Get file ${CLI OUTDIR}/debug.txt
Debug file should contain ${content}
- ... ${tc.kws[0].msgs[0].timestamp} - INFO - Known timestamp
- ... ${tc.kws[0].msgs[1].timestamp} - INFO - Current
+ ... ${tc[0, 0].timestamp} - INFO - Known timestamp
+ ... ${tc[0, 1].timestamp} - INFO - Current
Writing Non-ASCII To Debugfile
[Documentation] Tests also that '.txt' is appended if no extension given
diff --git a/atest/robot/cli/runner/exit_on_failure.robot b/atest/robot/cli/runner/exit_on_failure.robot
index 885747676a9..c11932cfe3b 100644
--- a/atest/robot/cli/runner/exit_on_failure.robot
+++ b/atest/robot/cli/runner/exit_on_failure.robot
@@ -73,12 +73,12 @@ Suite setup fails
[Setup] Run Tests
... --ExitOnFail --variable SUITE_SETUP:Fail
... misc/setups_and_teardowns.robot misc/pass_and_fail.robot
- Test Should Not Have Been Run Test with setup and teardown
- Test Should Not Have Been Run Test with failing setup
- Test Should Not Have Been Run Test with failing teardown
- Test Should Not Have Been Run Failing test with failing teardown
- Test Should Not Have Been Run Pass
- Test Should Not Have Been Run Fail
+ Parent Setup Should Have Failed Test with setup and teardown
+ Test Should Not Have Been Run Test with failing setup
+ Test Should Not Have Been Run Test with failing teardown
+ Test Should Not Have Been Run Failing test with failing teardown
+ Test Should Not Have Been Run Pass
+ Test Should Not Have Been Run Fail
Suite teardown fails
[Setup] Run Tests
@@ -96,6 +96,11 @@ Failure set by listener can initiate exit-on-failure
Test Should Not Have Been Run Fail
*** Keywords ***
+Parent Setup Should Have Failed
+ [Arguments] ${name}
+ ${tc} = Check Test Case ${name} FAIL Parent suite setup failed:\nAssertionError
+ Should Not Contain ${tc.tags} robot:exit
+
Test Should Not Have Been Run
[Arguments] ${name}
${tc} = Check Test Case ${name} FAIL ${EXIT ON FAILURE}
diff --git a/atest/robot/cli/runner/exit_on_failure_with_skip_on_failure.robot b/atest/robot/cli/runner/exit_on_failure_with_skip_on_failure.robot
new file mode 100644
index 00000000000..44ccf986f97
--- /dev/null
+++ b/atest/robot/cli/runner/exit_on_failure_with_skip_on_failure.robot
@@ -0,0 +1,49 @@
+*** Settings ***
+Resource atest_resource.robot
+
+*** Test Cases ***
+Exit-on-failure is not initiated if test fails and skip-on-failure is active
+ Run Tests --exit-on-failure --skip-on-failure skip-on-failure --include skip-on-failure running/skip/skip.robot
+ Should Contain Tests ${SUITE}
+ ... Skipped with --SkipOnFailure
+ ... Skipped with --SkipOnFailure when failure in setup
+ ... Skipped with --SkipOnFailure when failure in teardown
+
+Exit-on-failure is not initiated if suite setup fails and skip-on-failure is active with all tests
+ Run Tests --exit-on-failure --skip-on-failure tag1 --variable SUITE_SETUP:Fail
+ ... misc/setups_and_teardowns.robot misc/pass_and_fail.robot misc/pass_and_fail.robot
+ VAR ${message}
+ ... Failed test skipped using 'tag1' tag.
+ ...
+ ... Original failure:
+ ... Parent suite setup failed:
+ ... AssertionError
+ ... separator=\n
+ Should Contain Tests ${SUITE.suites[0]}
+ ... Test with setup and teardown=SKIP:${message}
+ ... Test with failing setup=SKIP:${message}
+ ... Test with failing teardown=SKIP:${message}
+ ... Failing test with failing teardown=SKIP:${message}
+ Should Contain Tests ${SUITE.suites[1]}
+ ... Pass
+ ... Fail
+ Should Contain Tests ${SUITE.suites[2]}
+ ... Pass=FAIL:Failure occurred and exit-on-failure mode is in use.
+ ... Fail=FAIL:Failure occurred and exit-on-failure mode is in use.
+
+Exit-on-failure is initiated if suite setup fails and skip-on-failure is not active with all tests
+ Run Tests --exit-on-failure --skip-on-failure tag2 --variable SUITE_SETUP:Fail
+ ... misc/setups_and_teardowns.robot misc/pass_and_fail.robot
+ VAR ${prefix}
+ ... Failed test skipped using 'tag2' tag.
+ ...
+ ... Original failure:
+ ... separator=\n
+ Should Contain Tests ${SUITE.suites[0]}
+ ... Test with setup and teardown=SKIP:${prefix}\nParent suite setup failed:\nAssertionError
+ ... Test with failing setup=FAIL:Parent suite setup failed:\nAssertionError
+ ... Test with failing teardown=SKIP:${prefix}\nFailure occurred and exit-on-failure mode is in use.
+ ... Failing test with failing teardown=SKIP:${prefix}\nFailure occurred and exit-on-failure mode is in use.
+ Should Contain Tests ${SUITE.suites[1]}
+ ... Pass=FAIL:Failure occurred and exit-on-failure mode is in use.
+ ... Fail=FAIL:Failure occurred and exit-on-failure mode is in use.
diff --git a/atest/robot/cli/runner/invalid_usage.robot b/atest/robot/cli/runner/invalid_usage.robot
index 38e76f41c35..739b6ea9be9 100644
--- a/atest/robot/cli/runner/invalid_usage.robot
+++ b/atest/robot/cli/runner/invalid_usage.robot
@@ -46,8 +46,8 @@ Invalid --RemoveKeywords
Invalid --loglevel
--loglevel bad tests.robot
- ... Invalid value for option '--loglevel': Invalid level 'BAD'.
+ ... Invalid value for option '--loglevel': Invalid log level 'BAD'.
--loglevel INFO:INV tests.robot
- ... Invalid value for option '--loglevel': Invalid level 'INV'.
+ ... Invalid value for option '--loglevel': Invalid log level 'INV'.
-L INFO:DEBUG tests.robot
... Invalid value for option '--loglevel': Level in log 'DEBUG' is lower than execution level 'INFO'.
diff --git a/atest/robot/cli/runner/log_level.robot b/atest/robot/cli/runner/log_level.robot
index b6033bcc6d7..76531894932 100644
--- a/atest/robot/cli/runner/log_level.robot
+++ b/atest/robot/cli/runner/log_level.robot
@@ -1,71 +1,73 @@
*** Settings ***
-Documentation Tests for setting log level from command line with --loglevel option. Setting log level while executing tests (BuiltIn.Set Log Level) is tested with BuiltIn library keywords.
-Resource atest_resource.robot
+Documentation Tests for setting log level from command line with --loglevel option.
+... Setting log level while executing tests (BuiltIn.Set Log Level) is
+... tested with BuiltIn library keywords.
+Resource atest_resource.robot
*** Variables ***
-${TESTDATA} misc/pass_and_fail.robot
-${LOG NAME} logfile.html
+${TESTDATA} misc/pass_and_fail.robot
+${LOG NAME} logfile.html
*** Test Cases ***
No Log Level Given
[Documentation] Default level of INFO should be used
Run Tests ${EMPTY} ${TESTDATA}
- Check Log Message ${SUITE.tests[0].kws[0].kws[0].msgs[0]} Hello says "Pass"! INFO
- Should Be Empty ${SUITE.tests[0].kws[0].kws[1].messages}
- Check Log Message ${SUITE.tests[1].kws[1].msgs[0]} Expected failure FAIL
+ Check Log Message ${SUITE.tests[0][0, 0, 0]} Hello says "Pass"! INFO
+ Should Be Empty ${SUITE.tests[0][0, 1].messages}
+ Check Log Message ${SUITE.tests[1][1, 0]} Expected failure FAIL
Trace Level
- Run Tests --loglevel TRACE ${TESTDATA}
+ Run Tests --loglevel TRACE ${TESTDATA}
Should Log On Trace Level
Debug Level
- Run Tests --loglevel debug --log ${LOG NAME} ${TESTDATA}
+ Run Tests --loglevel debug --log ${LOG NAME} ${TESTDATA}
Should Log On Debug Level
Min level should be 'DEBUG' and default 'DEBUG'
Debug Level With Default Info
- Run Tests --loglevel dEBug:iNfo --log ${LOG NAME} ${TESTDATA}
+ Run Tests --loglevel dEBug:iNfo --log ${LOG NAME} ${TESTDATA}
Should Log On Debug Level
Min level should be 'DEBUG' and default 'INFO'
Trace Level With Default Debug
- Run Tests --loglevel trace:Debug --log ${LOG NAME} ${TESTDATA}
+ Run Tests --loglevel trace:Debug --log ${LOG NAME} ${TESTDATA}
Should Log On Trace Level
Min level should be 'TRACE' and default 'DEBUG'
Info Level
Run Tests -L InFo ${TESTDATA}
- Check Log Message ${SUITE.tests[0].kws[0].kws[0].msgs[0]} Hello says "Pass"! INFO
- Should Be Empty ${SUITE.tests[0].kws[0].kws[1].messages}
- Check Log Message ${SUITE.tests[1].kws[1].msgs[0]} Expected failure FAIL
+ Check Log Message ${SUITE.tests[0][0, 0, 0]} Hello says "Pass"! INFO
+ Should Be Empty ${SUITE.tests[0][0, 1].messages}
+ Check Log Message ${SUITE.tests[1][1, 0]} Expected failure FAIL
Warn Level
Run Tests --loglevel WARN --variable LEVEL1:WARN --variable LEVEL2:INFO ${TESTDATA}
- Check Log Message ${SUITE.tests[0].kws[0].kws[0].msgs[0]} Hello says "Pass"! WARN
- Should Be Empty ${SUITE.tests[0].kws[0].kws[1].messages}
- Check Log Message ${SUITE.tests[1].kws[1].msgs[0]} Expected failure FAIL
+ Check Log Message ${SUITE.tests[0][0, 0, 0]} Hello says "Pass"! WARN
+ Should Be Empty ${SUITE.tests[0][0, 1].messages}
+ Check Log Message ${SUITE.tests[1][1, 0]} Expected failure FAIL
Warnings Should Be Written To Syslog
- Should Be Equal ${PREV TEST NAME} Warn Level
- Check Log Message ${ERRORS.msgs[0]} Hello says "Suite Setup"! WARN
- Check Log Message ${ERRORS.msgs[1]} Hello says "Pass"! WARN
- Check Log Message ${ERRORS.msgs[2]} Hello says "Fail"! WARN
- Length Should Be ${ERRORS.msgs} 3
- Syslog Should Contain | WARN \ | Hello says "Suite Setup"!
- Syslog Should Contain | WARN \ | Hello says "Pass"!
- Syslog Should Contain | WARN \ | Hello says "Fail"!
+ Should Be Equal ${PREV TEST NAME} Warn Level
+ Check Log Message ${ERRORS[0]} Hello says "Suite Setup"! WARN
+ Check Log Message ${ERRORS[1]} Hello says "Pass"! WARN
+ Check Log Message ${ERRORS[2]} Hello says "Fail"! WARN
+ Length Should Be ${ERRORS.messages} 3
+ Syslog Should Contain | WARN \ | Hello says "Suite Setup"!
+ Syslog Should Contain | WARN \ | Hello says "Pass"!
+ Syslog Should Contain | WARN \ | Hello says "Fail"!
Error Level
Run Tests --loglevel ERROR --variable LEVEL1:ERROR --variable LEVEL2:WARN ${TESTDATA}
- Check Log Message ${SUITE.tests[0].kws[0].kws[0].msgs[0]} Hello says "Pass"! ERROR
- Should Be Empty ${SUITE.tests[0].kws[0].kws[1].messages}
- Check Log Message ${SUITE.tests[1].kws[1].msgs[0]} Expected failure FAIL
+ Check Log Message ${SUITE.tests[0][0, 0, 0]} Hello says "Pass"! ERROR
+ Should Be Empty ${SUITE.tests[0][0, 1].messages}
+ Check Log Message ${SUITE.tests[1][1, 0]} Expected failure FAIL
None Level
Run Tests --loglevel NONE --log ${LOG NAME} --variable LEVEL1:ERROR --variable LEVEL2:WARN ${TESTDATA}
- Should Be Empty ${SUITE.tests[0].kws[0].kws[0].messages}
- Should Be Empty ${SUITE.tests[0].kws[0].kws[1].messages}
- Should Be Empty ${SUITE.tests[1].kws[1].messages}
+ Should Be Empty ${SUITE.tests[0][0, 0].message}
+ Should Be Empty ${SUITE.tests[0][0, 1].messages}
+ Should Be Empty ${SUITE.tests[1][1].messages}
Min level should be 'NONE' and default 'NONE'
*** Keywords ***
@@ -75,14 +77,14 @@ Min level should be '${min}' and default '${default}'
Should contain ${log} "defaultLevel":"${default}"
Should Log On Debug Level
- Check Log Message ${SUITE.tests[0].kws[0].kws[0].msgs[0]} Hello says "Pass"! INFO
- Check Log Message ${SUITE.tests[0].kws[0].kws[1].msgs[0]} Debug message DEBUG
- Check Log Message ${SUITE.tests[1].kws[1].msgs[0]} Expected failure FAIL
+ Check Log Message ${SUITE.tests[0][0, 0, 0]} Hello says "Pass"! INFO
+ Check Log Message ${SUITE.tests[0][0, 1, 0]} Debug message DEBUG
+ Check Log Message ${SUITE.tests[1][1, 0]} Expected failure FAIL
Should Log On Trace Level
- Check Log Message ${SUITE.tests[0].kws[0].kws[0].msgs[0]} Arguments: [ 'Hello says "Pass"!' | 'INFO' ] TRACE
- Check Log Message ${SUITE.tests[0].kws[0].kws[0].msgs[1]} Hello says "Pass"! INFO
- Check Log Message ${SUITE.tests[0].kws[0].kws[0].msgs[2]} Return: None TRACE
- Check Log Message ${SUITE.tests[0].kws[0].kws[1].msgs[1]} Debug message DEBUG
- Check Log Message ${SUITE.tests[1].kws[1].msgs[0]} Arguments: [ 'Expected failure' ] TRACE
- Check Log Message ${SUITE.tests[1].kws[1].msgs[1]} Expected failure FAIL
+ Check Log Message ${SUITE.tests[0][0, 1, 0]} Arguments: [ 'Hello says "Pass"!' | 'INFO' ] TRACE
+ Check Log Message ${SUITE.tests[0][0, 1, 1]} Hello says "Pass"! INFO
+ Check Log Message ${SUITE.tests[0][0, 1, 2]} Return: None TRACE
+ Check Log Message ${SUITE.tests[0][0, 2, 1]} Debug message DEBUG
+ Check Log Message ${SUITE.tests[1][1, 0]} Arguments: [ 'Expected failure' ] TRACE
+ Check Log Message ${SUITE.tests[1][1, 1]} Expected failure FAIL
diff --git a/atest/robot/cli/runner/output_files.robot b/atest/robot/cli/runner/output_files.robot
index c62afc2ba52..00006f221ad 100644
--- a/atest/robot/cli/runner/output_files.robot
+++ b/atest/robot/cli/runner/output_files.robot
@@ -15,13 +15,17 @@ Output And Log
Run Tests Without Processing Output --outputdir ${CLI OUTDIR} --output myoutput.xml --report none --log mylog.html ${TESTFILE}
Output Directory Should Contain mylog.html myoutput.xml
-Disabling output XML only disables log with a warning
+Disabling only output file disables log with a warning
Run Tests Without Processing Output --outputdir ${CLI OUTDIR} -o nOnE -r report.html -l mylog.html ${TESTFILE}
+ Stdout Should Contain Output: \ NONE\nReport:
+ Stderr Should Match Regexp \\[ ERROR \\] Log file cannot be created if output.xml is disabled.
Output Directory Should Contain report.html
- Stderr Should Match Regexp \\[ ERROR \\] Log file cannot be created if output.xml is disabled.
All output files disabled
- Run Tests Without Processing Output --outputdir ${CLI OUTDIR} -o nOnE -r NONE -l none ${TESTFILE}
+ [Documentation] Turning colors on turns also hyperlinks on console and `NONE` cannot be linked.
+ Run Tests Without Processing Output --outputdir ${CLI OUTDIR} -o nOnE -r NONE -l none --console-colors ON ${TESTFILE}
+ Stdout Should Contain Output: \ NONE\n
+ Stderr Should Be Empty
Output Directory Should Be Empty
Debug, Xunit And Report File Can Be Created When Output Is NONE
diff --git a/atest/robot/cli/runner/remove_keywords.robot b/atest/robot/cli/runner/remove_keywords.robot
index 1576131a8d7..05d1dca3f6a 100644
--- a/atest/robot/cli/runner/remove_keywords.robot
+++ b/atest/robot/cli/runner/remove_keywords.robot
@@ -3,65 +3,67 @@ Suite Setup Run Tests And Remove Keywords
Resource atest_resource.robot
*** Variables ***
-${PASS MESSAGE} -PASSED -ALL
-${FAIL MESSAGE} -ALL +PASSED
-${REMOVED FOR MESSAGE} -FOR -ALL
-${KEPT FOR MESSAGE} +FOR -ALL
-${REMOVED WHILE MESSAGE} -WHILE -ALL
-${KEPT WHILE MESSAGE} +WHILE -ALL
-${REMOVED WUKS MESSAGE} -WUKS -ALL
-${KEPT WUKS MESSAGE} +WUKS -ALL
-${REMOVED BY NAME MESSAGE} -BYNAME -ALL
-${KEPT BY NAME MESSAGE} +BYNAME -ALL
+${PASS MESSAGE} -PASSED -ALL
+${FAIL MESSAGE} -ALL +PASSED
+${REMOVED FOR MESSAGE} -FOR -ALL
+${KEPT FOR MESSAGE} +FOR -ALL
+${REMOVED WHILE MESSAGE} -WHILE -ALL
+${KEPT WHILE MESSAGE} +WHILE -ALL
+${REMOVED WUKS MESSAGE} -WUKS -ALL
+${KEPT WUKS MESSAGE} +WUKS -ALL
+${REMOVED BY NAME MESSAGE} -BYNAME -ALL
+${KEPT BY NAME MESSAGE} +BYNAME -ALL
${REMOVED BY PATTERN MESSAGE} -BYPATTERN -ALL
-${KEPT BY PATTERN MESSAGE} +BYPATTERN -ALL
+${KEPT BY PATTERN MESSAGE} +BYPATTERN -ALL
*** Test Cases ***
PASSED option when test passes
Log should not contain ${PASS MESSAGE}
Output should contain pass message
+ Messages from body are removed Passing
PASSED option when test fails
- Log should contain ${FAIL MESSAGE}
+ Log should contain ${FAIL MESSAGE}
Output should contain fail message
+ Messages from body are not removed Failing
FOR option
Log should not contain ${REMOVED FOR MESSAGE}
- Log should contain ${KEPT FOR MESSAGE}
+ Log should contain ${KEPT FOR MESSAGE}
Output should contain for messages
WHILE option
Log should not contain ${REMOVED WHILE MESSAGE}
- Log should contain ${KEPT WHILE MESSAGE}
+ Log should contain ${KEPT WHILE MESSAGE}
Output should contain while messages
WUKS option
Log should not contain ${REMOVED WUKS MESSAGE}
- Log should contain ${KEPT WUKS MESSAGE}
+ Log should contain ${KEPT WUKS MESSAGE}
Output should contain WUKS messages
NAME option
Log should not contain ${REMOVED BY NAME MESSAGE}
- Log should contain ${KEPT BY NAME MESSAGE}
+ Log should contain ${KEPT BY NAME MESSAGE}
Output should contain NAME messages
NAME option with pattern
Log should not contain ${REMOVED BY PATTERN MESSAGE}
- Log should contain ${KEPT BY PATTERN MESSAGE}
+ Log should contain ${KEPT BY PATTERN MESSAGE}
Output should contain NAME messages with patterns
TAGged keywords
- Log should contain This is not removed by TAG
+ Log should contain This is not removed by TAG
Log should not contain This is removed by TAG
Warnings and errors are preserved
+ Log should contain Keywords with warnings are not removed
+ Log should contain Keywords with errors are not removed
Output should contain warning and error
- Log should contain Keywords with warnings are not removed
- Log should contain Keywords with errors are not removed
*** Keywords ***
Run tests and remove keywords
- ${opts} = Catenate
+ VAR ${opts}
... --removekeywords passed
... --RemoveKeywords FoR
... --RemoveKeywords whiLE
@@ -70,10 +72,11 @@ Run tests and remove keywords
... --removekeywords name:Thisshouldbe*
... --removekeywords name:Remove???
... --removekeywords tag:removeANDkitty
+ ... --listener AddMessagesToTestBody
... --log log.html
Run tests ${opts} cli/remove_keywords/all_combinations.robot
- ${LOG} = Get file ${OUTDIR}/log.html
- Set suite variable $LOG
+ ${log} = Get file ${OUTDIR}/log.html
+ VAR ${LOG} ${log} scope=SUITE
Log should not contain
[Arguments] ${msg}
@@ -83,13 +86,23 @@ Log should contain
[Arguments] ${msg}
Should contain ${LOG} ${msg}
+Messages from body are removed
+ [Arguments] ${name}
+ Log should not contain Hello '${name}', says listener!
+ Log should not contain Bye '${name}', says listener!
+
+Messages from body are not removed
+ [Arguments] ${name}
+ Log should contain Hello '${name}', says listener!
+ Log should contain Bye '${name}', says listener!
+
Output should contain pass message
${tc} = Check test case Passing
- Check Log Message ${tc.kws[0].msgs[0]} ${PASS MESSAGE}
+ Check Log Message ${tc[1, 0]} ${PASS MESSAGE}
Output should contain fail message
${tc} = Check test case Failing
- Check Log Message ${tc.kws[0].msgs[0]} ${FAIL MESSAGE}
+ Check Log Message ${tc[1, 0]} ${FAIL MESSAGE}
Output should contain for messages
Test should contain for messages FOR when test passes
@@ -98,11 +111,10 @@ Output should contain for messages
Test should contain for messages
[Arguments] ${name}
${tc} = Check test case ${name}
- ${for} = Set Variable ${tc.kws[0].kws[0]}
- Check log message ${for.body[0].body[0].body[1].body[0].body[0]} ${REMOVED FOR MESSAGE} one
- Check log message ${for.body[1].body[0].body[1].body[0].body[0]} ${REMOVED FOR MESSAGE} two
- Check log message ${for.body[2].body[0].body[1].body[0].body[0]} ${REMOVED FOR MESSAGE} three
- Check log message ${for.body[3].body[0].body[0].body[0].body[0]} ${KEPT FOR MESSAGE} LAST
+ Check log message ${tc[1, 0, 0, 0, 1, 0, 0]} ${REMOVED FOR MESSAGE} one
+ Check log message ${tc[1, 0, 1, 0, 1, 0, 0]} ${REMOVED FOR MESSAGE} two
+ Check log message ${tc[1, 0, 2, 0, 1, 0, 0]} ${REMOVED FOR MESSAGE} three
+ Check log message ${tc[1, 0, 3, 0, 0, 0, 0]} ${KEPT FOR MESSAGE} LAST
Output should contain while messages
Test should contain while messages WHILE when test passes
@@ -111,11 +123,10 @@ Output should contain while messages
Test should contain while messages
[Arguments] ${name}
${tc} = Check test case ${name}
- ${while} = Set Variable ${tc.kws[0].kws[1]}
- Check log message ${while.body[0].body[0].body[1].body[0].body[0]} ${REMOVED WHILE MESSAGE} 1
- Check log message ${while.body[1].body[0].body[1].body[0].body[0]} ${REMOVED WHILE MESSAGE} 2
- Check log message ${while.body[2].body[0].body[1].body[0].body[0]} ${REMOVED WHILE MESSAGE} 3
- Check log message ${while.body[3].body[0].body[0].body[0].body[0]} ${KEPT WHILE MESSAGE} 4
+ Check log message ${tc[1, 1, 0, 0, 1, 0, 0]} ${REMOVED WHILE MESSAGE} 1
+ Check log message ${tc[1, 1, 1, 0, 1, 0, 0]} ${REMOVED WHILE MESSAGE} 2
+ Check log message ${tc[1, 1, 2, 0, 1, 0, 0]} ${REMOVED WHILE MESSAGE} 3
+ Check log message ${tc[1, 1, 3, 0, 0, 0, 0]} ${KEPT WHILE MESSAGE} 4
Output should contain WUKS messages
Test should contain WUKS messages WUKS when test passes
@@ -124,9 +135,9 @@ Output should contain WUKS messages
Test should contain WUKS messages
[Arguments] ${name}
${tc} = Check test case ${name}
- Check log message ${tc.kws[0].kws[0].kws[1].kws[0].msgs[0]} ${REMOVED WUKS MESSAGE} FAIL
- Check log message ${tc.kws[0].kws[8].kws[1].kws[0].msgs[0]} ${REMOVED WUKS MESSAGE} FAIL
- Check log message ${tc.kws[0].kws[9].kws[2].kws[0].msgs[0]} ${KEPT WUKS MESSAGE} FAIL
+ Check log message ${tc[1, 0, 1, 0, 0]} ${REMOVED WUKS MESSAGE} FAIL
+ Check log message ${tc[1, 8, 1, 0, 0]} ${REMOVED WUKS MESSAGE} FAIL
+ Check log message ${tc[1, 9, 2, 0, 0]} ${KEPT WUKS MESSAGE} FAIL
Output should contain NAME messages
Test should contain NAME messages NAME when test passes
@@ -135,10 +146,10 @@ Output should contain NAME messages
Test should contain NAME messages
[Arguments] ${name}
${tc}= Check test case ${name}
- Check log message ${tc.kws[0].kws[0].msgs[0]} ${REMOVED BY NAME MESSAGE}
- Check log message ${tc.kws[1].kws[0].msgs[0]} ${REMOVED BY NAME MESSAGE}
- Check log message ${tc.kws[2].kws[0].kws[0].msgs[0]} ${REMOVED BY NAME MESSAGE}
- Check log message ${tc.kws[2].kws[1].msgs[0]} ${KEPT BY NAME MESSAGE}
+ Check log message ${tc[1, 0, 0]} ${REMOVED BY NAME MESSAGE}
+ Check log message ${tc[2, 0, 0]} ${REMOVED BY NAME MESSAGE}
+ Check log message ${tc[3, 0, 0, 0]} ${REMOVED BY NAME MESSAGE}
+ Check log message ${tc[3, 1, 0]} ${KEPT BY NAME MESSAGE}
Output should contain NAME messages with patterns
Test should contain NAME messages with * pattern NAME with * pattern when test passes
@@ -149,20 +160,20 @@ Output should contain NAME messages with patterns
Test should contain NAME messages with * pattern
[Arguments] ${name}
${tc}= Check test case ${name}
- Check log message ${tc.kws[0].kws[0].msgs[0]} ${REMOVED BY PATTERN MESSAGE}
- Check log message ${tc.kws[1].kws[0].msgs[0]} ${REMOVED BY PATTERN MESSAGE}
- Check log message ${tc.kws[2].kws[0].msgs[0]} ${REMOVED BY PATTERN MESSAGE}
- Check log message ${tc.kws[3].kws[0].kws[0].msgs[0]} ${REMOVED BY PATTERN MESSAGE}
- Check log message ${tc.kws[3].kws[1].msgs[0]} ${KEPT BY PATTERN MESSAGE}
+ Check log message ${tc[1, 0, 0]} ${REMOVED BY PATTERN MESSAGE}
+ Check log message ${tc[2, 0, 0]} ${REMOVED BY PATTERN MESSAGE}
+ Check log message ${tc[3, 0, 0]} ${REMOVED BY PATTERN MESSAGE}
+ Check log message ${tc[4, 0, 0, 0]} ${REMOVED BY PATTERN MESSAGE}
+ Check log message ${tc[4, 1, 0]} ${KEPT BY PATTERN MESSAGE}
Test should contain NAME messages with ? pattern
[Arguments] ${name}
${tc}= Check test case ${name}
- Check log message ${tc.kws[0].kws[0].msgs[0]} ${REMOVED BY PATTERN MESSAGE}
- Check log message ${tc.kws[1].kws[0].kws[0].msgs[0]} ${REMOVED BY PATTERN MESSAGE}
- Check log message ${tc.kws[1].kws[1].msgs[0]} ${KEPT BY PATTERN MESSAGE}
+ Check log message ${tc[1, 0, 0]} ${REMOVED BY PATTERN MESSAGE}
+ Check log message ${tc[2, 0, 0, 0]} ${REMOVED BY PATTERN MESSAGE}
+ Check log message ${tc[2, 1, 0]} ${KEPT BY PATTERN MESSAGE}
Output should contain warning and error
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].kws[0].kws[0].msgs[0]} Keywords with warnings are not removed WARN
- Check Log Message ${tc.kws[1].kws[0].msgs[0]} Keywords with errors are not removed ERROR
+ Check Log Message ${tc[1, 0, 0, 0]} Keywords with warnings are not removed WARN
+ Check Log Message ${tc[2, 0, 0]} Keywords with errors are not removed ERROR
diff --git a/atest/robot/cli/runner/rerunfailed.robot b/atest/robot/cli/runner/rerunfailed.robot
index adacf4a0d7b..68d263d8d34 100644
--- a/atest/robot/cli/runner/rerunfailed.robot
+++ b/atest/robot/cli/runner/rerunfailed.robot
@@ -49,7 +49,7 @@ Suite initialization
Run Tests ${EMPTY} ${SUITE DIR}
Copy File ${OUTFILE} ${RUN FAILED FROM}
Copy File ${ORIG DIR}/runfailed2.robot ${SUITE DIR}/runfailed.robot
- Run Tests --rerunfailed ${RUN FAILED FROM} --test Selected --exclude excluded_tag ${SUITE DIR}
+ Run Tests --rerunfailed ${RUN FAILED FROM} --test Selected --include common --exclude excluded_tag ${SUITE DIR}
Test Should Have Been Executed
[Arguments] ${name}
diff --git a/atest/robot/core/binary_data.robot b/atest/robot/core/binary_data.robot
index e3372e88519..f6c70ccc3bb 100644
--- a/atest/robot/core/binary_data.robot
+++ b/atest/robot/core/binary_data.robot
@@ -28,13 +28,13 @@ Print Bytes
... 116 t
... 123 {
... 127 \x7f
- Check Log Message ${tc.kws[0].msgs[${index}]} Byte ${index}: '${exp}'
+ Check Log Message ${tc[0, ${index}]} Byte ${index}: '${exp}'
END
# Check that all bytes were really written without errors.
FOR ${index} IN RANGE 256
- Should Start With ${tc.kws[0].msgs[${index}].message} Byte ${index}:
+ Should Start With ${tc[0, ${index}].message} Byte ${index}:
END
- Check Log Message ${tc.kws[0].msgs[-1]} All bytes printed successfully
+ Check Log Message ${tc[0, -1]} All bytes printed successfully
Byte Error
[Documentation] Check an exception containing control chars is handled ok
@@ -46,7 +46,7 @@ Byte Error In Setup And Teardown
Binary Data
[Documentation] Make sure even totally binary data doesn't break anything
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[1]} Binary data printed successfully
+ Check Log Message ${tc[0, 1]} Binary data printed successfully
*** Keywords ***
My Run Tests
diff --git a/atest/robot/core/filter_by_names.robot b/atest/robot/core/filter_by_names.robot
index a85d64e3876..221f274127c 100644
--- a/atest/robot/core/filter_by_names.robot
+++ b/atest/robot/core/filter_by_names.robot
@@ -17,16 +17,10 @@ ${SUITE DIR} misc/suites
Run And Check Tests --test *one --test Fi?st First Second One Third One
Run And Check Tests --test [Great]Lob[sterB]estCase[!3-9] GlobTestCase1 GlobTestCase2
---test is cumulative with --include
- Run And Check Tests --test fifth --include t1 First Fifth
-
---exclude wins ovet --test
- Run And Check Tests --test fi* --exclude t1 Fifth
-
--test not matching
Run Failing Test
... Suite 'Many Tests' contains no tests matching name 'notexists'.
- ... --test notexists ${SUITE FILE}
+ ... --test notexists
--test not matching with multiple inputs
Run Failing Test
@@ -36,6 +30,18 @@ ${SUITE DIR} misc/suites
... Suite 'My Name' contains no tests matching name 'notexists'.
... --name "My Name" --test notexists ${SUITE FILE} ${SUITE DIR}
+--test and --include must both match
+ Run And Check Tests --test first --include t1 -i f1 First
+ Run Failing Test
+ ... Suite 'Many Tests' contains no tests matching name 'fifth' and matching tag 't1'.
+ ... --test fifth --include t1
+
+--exclude wins over --test
+ Run And Check Tests --test fi* --exclude t1 Fifth
+ Run Failing Test
+ ... Suite 'Many Tests' contains no tests matching name 'first' and not matching tag 'f1'.
+ ... --test first --exclude f1
+
--suite once
Run Suites --suite tsuite1
Should Contain Suites ${SUITE} TSuite1
@@ -60,7 +66,7 @@ ${SUITE DIR} misc/suites
Parent suite init files are processed
Previous Test Should Have Passed --suite with patterns
Should Be True ${SUITE.teardown}
- Check log message ${SUITE.teardown.msgs[0]} Default suite teardown
+ Check log message ${SUITE.teardown[0]} Default suite teardown
--suite matching directory
Run Suites --suite sub?uit[efg]s
@@ -131,7 +137,7 @@ Parent suite init files are processed
Should Contain Tests ${SUITE} Suite1 First Suite3 First
--suite, --test, --include and --exclude
- Run Suites --suite sub* --suite "custom name *" --test *first -s nomatch -t nomatch --include sub3 --exclude t1
+ Run Suites --suite sub* --suite "custom name *" --test "subsuite3 second" -t *first -s nomatch -t nomatch --include f1 --exclude t1
Should Contain Suites ${SUITE} Custom name for 📂 'subsuites2' Subsuites
Should Contain Tests ${SUITE} SubSuite2 First SubSuite3 Second
@@ -162,6 +168,6 @@ Run Suites
Stderr Should Be Empty
Run Failing Test
- [Arguments] ${error} ${options} ${sources}
+ [Arguments] ${error} ${options} ${sources}=${SUITE FILE}
Run Tests Without Processing Output ${options} ${sources}
Stderr Should Be Equal To [ ERROR ] ${error}${USAGE TIP}\n
diff --git a/atest/robot/core/keyword_setup.robot b/atest/robot/core/keyword_setup.robot
index f7ba4f05495..1f8f91963f4 100644
--- a/atest/robot/core/keyword_setup.robot
+++ b/atest/robot/core/keyword_setup.robot
@@ -5,42 +5,42 @@ Resource atest_resource.robot
*** Test Cases ***
Passing setup
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.body[0].setup.msgs[0]} Hello, setup!
+ Check Log Message ${tc[0].setup[0]} Hello, setup!
Failing setup
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.body[0].setup.msgs[0]} Hello, setup! FAIL
- Should Be Equal ${tc.body[0].body[0].status} NOT RUN
+ Check Log Message ${tc[0].setup[0]} Hello, setup! FAIL
+ Should Be Equal ${tc[0, 0].status} NOT RUN
Failing setup and passing teardown
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.setup.setup.msgs[0]} Hello, setup! FAIL
- Should Be Equal ${tc.setup.body[0].status} NOT RUN
- Check Log Message ${tc.setup.teardown.msgs[0]} Hello, teardown! INFO
+ Check Log Message ${tc.setup.setup[0]} Hello, setup! FAIL
+ Should Be Equal ${tc.setup[0].status} NOT RUN
+ Check Log Message ${tc.setup.teardown[0]} Hello, teardown! INFO
Failing setup and teardown
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.body[0].setup.msgs[0]} Hello, setup! FAIL
- Should Be Equal ${tc.body[0].body[0].status} NOT RUN
- Check Log Message ${tc.body[0].teardown.msgs[0]} Hello, teardown! FAIL
+ Check Log Message ${tc[0].setup[0]} Hello, setup! FAIL
+ Should Be Equal ${tc[0, 0].status} NOT RUN
+ Check Log Message ${tc[0].teardown[0]} Hello, teardown! FAIL
Continue-on-failure mode is not enabled in setup
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.setup.setup.body[0].msgs[0]} Hello, setup! INFO
- Check Log Message ${tc.setup.setup.body[1].msgs[0]} Hello again, setup! FAIL
- Should Be Equal ${tc.setup.setup.body[2].status} NOT RUN
+ Check Log Message ${tc.setup.setup[0, 0]} Hello, setup! INFO
+ Check Log Message ${tc.setup.setup[1, 0]} Hello again, setup! FAIL
+ Should Be Equal ${tc.setup.setup[2].status} NOT RUN
NONE is same as no setup
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.body[0].setup.name} ${None}
+ Should Be Equal ${tc[0].setup.name} ${None}
Empty [Setup] is same as no setup
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.body[0].setup.name} ${None}
+ Should Be Equal ${tc[0].setup.name} ${None}
Using variable
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.body[0].setup.name} Log
- Should Be Equal ${tc.body[1].setup.name} ${None}
- Should Be Equal ${tc.body[2].setup.name} ${None}
- Should Be Equal ${tc.body[3].setup.name} Fail
+ Should Be Equal ${tc[0].setup.name} Log
+ Should Be Equal ${tc[1].setup.name} ${None}
+ Should Be Equal ${tc[2].setup.name} ${None}
+ Should Be Equal ${tc[3].setup.name} Fail
diff --git a/atest/robot/core/keyword_teardown.robot b/atest/robot/core/keyword_teardown.robot
index 4c0506c3ef1..d15cfe8bb28 100644
--- a/atest/robot/core/keyword_teardown.robot
+++ b/atest/robot/core/keyword_teardown.robot
@@ -1,67 +1,67 @@
*** Settings ***
-Resource atest_resource.robot
-Suite Setup Run Tests ${EMPTY} core/keyword_teardown.robot
+Resource atest_resource.robot
+Suite Setup Run Tests ${EMPTY} core/keyword_teardown.robot
*** Test Cases ***
Passing Keyword with Teardown
- ${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} In UK
- Check Log Message ${tc.kws[0].teardown.msgs[0]} In UK Teardown
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Log Message ${tc[0, 0, 0]} In UK
+ Check Log Message ${tc[0].teardown[0]} In UK Teardown
Failing Keyword with Teardown
- ${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} Expected Failure! FAIL
- Check Log Message ${tc.kws[0].teardown.msgs[0]} In Failing UK Teardown
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Log Message ${tc[0, 0, 0]} Expected Failure! FAIL
+ Check Log Message ${tc[0].teardown[0]} In Failing UK Teardown
Teardown in keyword with embedded arguments
- ${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} In UK with Embedded Arguments
- Check Log Message ${tc.kws[0].teardown.msgs[0]} In Teardown of UK with Embedded Arguments
- Check Log Message ${tc.kws[1].kws[0].msgs[0]} Expected Failure in UK with Embedded Arguments FAIL
- Check Log Message ${tc.kws[1].teardown.msgs[0]} In Teardown of Failing UK with Embedded Arguments
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Log Message ${tc[0, 0, 0]} In UK with Embedded Arguments
+ Check Log Message ${tc[0].teardown[0]} In Teardown of UK with Embedded Arguments
+ Check Log Message ${tc[1, 0, 0]} Expected Failure in UK with Embedded Arguments FAIL
+ Check Log Message ${tc[1].teardown[0]} In Teardown of Failing UK with Embedded Arguments
Failure in Keyword Teardown
- ${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} In UK
- Check Log Message ${tc.kws[0].teardown.msgs[0]} Failing in UK Teardown FAIL
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Log Message ${tc[0, 0, 0]} In UK
+ Check Log Message ${tc[0].teardown[0]} Failing in UK Teardown FAIL
Failures in Keyword and Teardown
- ${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} Expected Failure! FAIL
- Check Log Message ${tc.kws[0].teardown.msgs[0]} Failing in UK Teardown FAIL
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Log Message ${tc[0, 0, 0]} Expected Failure! FAIL
+ Check Log Message ${tc[0].teardown[0]} Failing in UK Teardown FAIL
Multiple Failures in Keyword Teardown
- ${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].teardown.kws[0].msgs[0]} Failure in Teardown FAIL
- Check Log Message ${tc.kws[0].teardown.kws[1].kws[0].msgs[0]} Expected Failure! FAIL
- Check Log Message ${tc.kws[0].teardown.kws[1].kws[1].msgs[0]} Executed if in nested Teardown
- Check Log Message ${tc.kws[0].teardown.kws[2].msgs[0]} Third failure in Teardown FAIL
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Log Message ${tc[0].teardown[0, 0]} Failure in Teardown FAIL
+ Check Log Message ${tc[0].teardown[1, 0, 0]} Expected Failure! FAIL
+ Check Log Message ${tc[0].teardown[1, 1, 0]} Executed if in nested Teardown
+ Check Log Message ${tc[0].teardown[2, 0]} Third failure in Teardown FAIL
Nested Keyword Teardowns
- ${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].kws[0].msgs[0]} In UK
- Check Log Message ${tc.kws[0].kws[0].teardown.msgs[0]} In UK Teardown
- Check Log Message ${tc.kws[0].teardown.kws[0].msgs[0]} In UK
- Check Log Message ${tc.kws[0].teardown.teardown.msgs[0]} In UK Teardown
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Log Message ${tc[0, 0, 0, 0]} In UK
+ Check Log Message ${tc[0, 0].teardown[0]} In UK Teardown
+ Check Log Message ${tc[0].teardown[0, 0]} In UK
+ Check Log Message ${tc[0].teardown.teardown[0]} In UK Teardown
Nested Keyword Teardown Failures
- ${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].teardown.msgs[0]} Failing in UK Teardown FAIL
- Check Log Message ${tc.kws[0].teardown.msgs[0]} Failing in outer UK Teardown FAIL
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Log Message ${tc[0, 0].teardown[0]} Failing in UK Teardown FAIL
+ Check Log Message ${tc[0].teardown[0]} Failing in outer UK Teardown FAIL
Continuable Failure in Keyword
- ${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].kws[0].msgs[0]} Please continue FAIL
- Check Log Message ${tc.kws[0].kws[1].msgs[0]} After continuable failure
- Check Log Message ${tc.kws[0].teardown.msgs[0]} In UK Teardown
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Log Message ${tc[0, 0, 0, 0]} Please continue FAIL
+ Check Log Message ${tc[0, 1, 0]} After continuable failure
+ Check Log Message ${tc[0].teardown[0]} In UK Teardown
Non-ASCII Failure in Keyword Teardown
- ${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} åäö
- Check Log Message ${tc.kws[0].teardown.msgs[0]} Hyvää äitienpäivää! FAIL
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Log Message ${tc[0, 0, 0]} åäö
+ Check Log Message ${tc[0].teardown[0]} Hyvää äitienpäivää! FAIL
Keyword cannot have only teardown
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Replacing Variables in Keyword Teardown Fails
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME}
diff --git a/atest/robot/core/non_ascii.robot b/atest/robot/core/non_ascii.robot
index 0d59f975c0b..732acc79e9b 100644
--- a/atest/robot/core/non_ascii.robot
+++ b/atest/robot/core/non_ascii.robot
@@ -6,41 +6,41 @@ Variables unicode_vars.py
*** Test Cases ***
Non-ASCII Log Messages
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} ${MESSAGE1}
- Check Log Message ${tc.kws[0].msgs[1]} ${MESSAGE2}
- Check Log Message ${tc.kws[0].msgs[2]} ${MESSAGE3}
+ Check Log Message ${tc[0, 0]} ${MESSAGE1}
+ Check Log Message ${tc[0, 1]} ${MESSAGE2}
+ Check Log Message ${tc[0, 2]} ${MESSAGE3}
Non-ASCII Return Value
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[2].msgs[0]} Français
+ Check Log Message ${tc[2, 0]} Français
Non-ASCII In Return Value Attributes
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} ${MESSAGES}
- Check Log Message ${tc.kws[0].msgs[1]} \${obj} = ${MESSAGES}
- Check Log Message ${tc.kws[1].msgs[0]} ${MESSAGES}
+ Check Log Message ${tc[0, 0]} ${MESSAGES}
+ Check Log Message ${tc[0, 1]} \${obj} = ${MESSAGES}
+ Check Log Message ${tc[1, 0]} ${MESSAGES}
Non-ASCII Failure
${tc} = Check Test Case ${TESTNAME} FAIL ${MESSAGES}
- Check Log Message ${tc.kws[0].msgs[0]} ${MESSAGES} FAIL
+ Check Log Message ${tc[0, 0]} ${MESSAGES} FAIL
Non-ASCII Failure In Setup
${tc} = Check Test Case ${TESTNAME} FAIL Setup failed:\n${MESSAGES}
- Check Log Message ${tc.setup.msgs[0]} ${MESSAGES} FAIL
+ Check Log Message ${tc.setup[0]} ${MESSAGES} FAIL
Non-ASCII Failure In Teardown
${tc} = Check Test Case ${TESTNAME} FAIL Teardown failed:\n${MESSAGES}
- Check Log Message ${tc.teardown.msgs[0]} ${MESSAGES} FAIL
+ Check Log Message ${tc.teardown[0]} ${MESSAGES} FAIL
Non-ASCII Failure In Teardown After Normal Failure
Check Test Case ${TESTNAME} FAIL Just ASCII here\n\nAlso teardown failed:\n${MESSAGES}
Ñöñ-ÄŚÇÏÏ Tëśt äņd Këywörd Nämës, Спасибо
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.kws[0].name} Ñöñ-ÄŚÇÏÏ Këywörd Nämë
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} Hyvää päivää
+ Should Be Equal ${tc[0].name} Ñöñ-ÄŚÇÏÏ Këywörd Nämë
+ Check Log Message ${tc[0, 0, 0]} Hyvää päivää
Non-ASCII Failure In Suite Setup and Teardown
Check Test Case ${TESTNAME}
- Check Log Message ${SUITE.suites[1].setup.msgs[0]} ${MESSAGES} FAIL
- Check Log Message ${SUITE.suites[1].teardown.msgs[0]} ${MESSAGES} FAIL
+ Check Log Message ${SUITE.suites[1].setup[0]} ${MESSAGES} FAIL
+ Check Log Message ${SUITE.suites[1].teardown[0]} ${MESSAGES} FAIL
diff --git a/atest/robot/core/overriding_default_settings_with_none.robot b/atest/robot/core/overriding_default_settings_with_none.robot
index 9e3dae2bb86..627d11d77b0 100644
--- a/atest/robot/core/overriding_default_settings_with_none.robot
+++ b/atest/robot/core/overriding_default_settings_with_none.robot
@@ -22,15 +22,15 @@ Overriding Test Teardown from Command Line
Overriding Test Template
${tc}= Check Test Case ${TESTNAME}
- Should Be Equal ${tc.body[0].full_name} BuiltIn.No Operation
+ Should Be Equal ${tc[0].full_name} BuiltIn.No Operation
Overriding Test Timeout
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.body[0].msgs[0]} Slept 123 milliseconds.
+ Check Log Message ${tc[0, 0]} Slept 123 milliseconds.
Overriding Test Timeout from Command Line
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.body[0].msgs[0]} Slept 123 milliseconds.
+ Check Log Message ${tc[0, 0]} Slept 123 milliseconds.
Overriding Default Tags
${tc}= Check Test Case ${TESTNAME}
@@ -44,5 +44,5 @@ Overriding Is Case Insensitive
${tc}= Check Test Case ${TESTNAME}
Setup Should Not Be Defined ${tc}
Teardown Should Not Be Defined ${tc}
- Should Be Equal ${tc.body[0].full_name} BuiltIn.No Operation
+ Should Be Equal ${tc[0].full_name} BuiltIn.No Operation
Should Be Empty ${tc.tags}
diff --git a/atest/robot/core/suite_setup_and_teardown.robot b/atest/robot/core/suite_setup_and_teardown.robot
index 55428c0caf1..11748ec4cf8 100644
--- a/atest/robot/core/suite_setup_and_teardown.robot
+++ b/atest/robot/core/suite_setup_and_teardown.robot
@@ -1,35 +1,32 @@
*** Settings ***
-Resource atest_resource.robot
+Resource atest_resource.robot
*** Variables ***
-${1 PASS MSG} 1 test, 1 passed, 0 failed
-${1 FAIL MSG} 1 test, 0 passed, 1 failed
-${2 FAIL MSG} 2 tests, 0 passed, 2 failed
-${4 FAIL MSG} 4 tests, 0 passed, 4 failed
-${5 FAIL MSG} 5 tests, 0 passed, 5 failed
-${12 FAIL MSG} 12 tests, 0 passed, 12 failed
-${ALSO} \n\nAlso teardown of the parent suite failed.
+${1 PASS MSG} 1 test, 1 passed, 0 failed
+${1 FAIL MSG} 1 test, 0 passed, 1 failed
+${2 FAIL MSG} 2 tests, 0 passed, 2 failed
+${4 FAIL MSG} 4 tests, 0 passed, 4 failed
+${5 FAIL MSG} 5 tests, 0 passed, 5 failed
+${12 FAIL MSG} 12 tests, 0 passed, 12 failed
+${ALSO} \n\nAlso teardown of the parent suite failed.
${EXECUTED FILE} %{TEMPDIR}/robot-suite-teardown-executed.txt
*** Test Cases ***
Passing Suite Setup
Run Tests ${EMPTY} core/passing_suite_setup.robot
- Check Suite Status ${SUITE} PASS ${1 PASS MSG}
- ... Verify Suite Setup
+ Check Suite Status ${SUITE} PASS ${1 PASS MSG} Verify Suite Setup
Passing Suite Teardown
[Setup] Remove File ${EXECUTED FILE}
Run Tests ${EMPTY} core/passing_suite_teardown.robot
- Check Suite Status ${SUITE} PASS ${1 PASS MSG}
- ... Test
+ Check Suite Status ${SUITE} PASS ${1 PASS MSG} Test
File Should Exist ${EXECUTED FILE}
[Teardown] Remove File ${EXECUTED FILE}
Passing Suite Setup And Teardown
[Setup] Remove File ${EXECUTED FILE}
Run Tests ${EMPTY} core/passing_suite_setup_and_teardown.robot
- Check Suite Status ${SUITE} PASS ${1 PASS MSG}
- ... Verify Suite Setup
+ Check Suite Status ${SUITE} PASS ${1 PASS MSG} Verify Suite Setup
File Should Exist ${EXECUTED FILE}
[Teardown] Remove File ${EXECUTED FILE}
@@ -38,11 +35,10 @@ Failing Suite Setup
Check Suite Status ${SUITE} FAIL
... Suite setup failed:\nExpected failure\n\n${2 FAIL MSG}
... Test 1 Test 2
- Should Be Equal ${SUITE.setup.status} FAIL
- Should Be Equal ${SUITE.teardown.status} PASS
- Length Should Be ${SUITE.teardown.msgs} 1
+ Should Be Equal ${SUITE.setup.status} FAIL
+ Should Be Equal ${SUITE.teardown.status} PASS
+ Length Should Be ${SUITE.teardown.body} 1
Check Log Message ${SUITE.teardown.messages[0]} Suite teardown executed
- Should Be Empty ${SUITE.teardown.kws}
Erroring Suite Setup
Run Tests ${EMPTY} core/erroring_suite_setup.robot
@@ -50,15 +46,15 @@ Erroring Suite Setup
... Suite setup failed:\nNo keyword with name 'Non-Existing Keyword' found.\n\n${2 FAIL MSG}
... Test 1 Test 2
Should Be Equal ${SUITE.setup.status} FAIL
- ${td} = Set Variable ${SUITE.teardown}
- Should Be Equal ${td.name} My TD
- Should Be Equal ${td.status} PASS
- Should Be Empty ${td.msgs}
- Length Should Be ${td.kws} 2
- Length Should Be ${td.kws[0].msgs} 1
- Check Log Message ${td.kws[0].msgs[0]} Hello from suite teardown!
- Should Be Empty ${td.kws[0].kws}
- Should Be Equal ${td.kws[1].full_name} BuiltIn.No Operation
+ VAR ${td} ${SUITE.teardown}
+ Should Be Equal ${td.name} My TD
+ Should Be Equal ${td.status} PASS
+ Length Should Be ${td.body} 2
+ Length Should Be ${td.messages} 0
+ Length Should Be ${td[0].body} 1
+ Length Should Be ${td[0].messages} 1
+ Check Log Message ${td[0, 0]} Hello from suite teardown!
+ Should Be Equal ${td[1].full_name} BuiltIn.No Operation
Failing Higher Level Suite Setup
Run Tests ${EMPTY} core/failing_higher_level_suite_setup
@@ -85,6 +81,18 @@ Failing Suite Teardown
Should Be Equal ${SUITE.teardown.status} FAIL
Output should contain teardown error ${error}
+Failing Suite Teardown when using JSON
+ Run Tests --output output.json core/failing_suite_teardown.robot output=${OUTDIR}/output.json
+ ${error} = Catenate SEPARATOR=\n\n
+ ... Several failures occurred:
+ ... 1) first
+ ... 2) second
+ Check Suite Status ${SUITE} FAIL
+ ... Suite teardown failed:\n${error}\n\n3 tests, 0 passed, 2 failed, 1 skipped
+ ... Passing Failing Skipping
+ Should Be Equal ${SUITE.teardown.status} FAIL
+ JSON output should contain teardown error ${error}
+
Erroring Suite Teardown
Run Tests ${EMPTY} core/erroring_suite_teardown.robot
Check Suite Status ${SUITE} FAIL
@@ -104,7 +112,7 @@ Failing Suite Setup And Teardown
... in two lines
Check Suite Status ${SUITE} FAIL ${error}\n\n${2 FAIL MSG}
... Test 1 Test 2
- Should Be Equal ${SUITE.setup.status} FAIL
+ Should Be Equal ${SUITE.setup.status} FAIL
Should Be Equal ${SUITE.teardown.status} FAIL
Output should contain teardown error Teardown failure\nin two lines
@@ -161,10 +169,14 @@ Long Error Messages
*** Keywords ***
Check Suite Status
[Arguments] ${suite or name} ${status} ${message} @{tests}
- ${is string} = Run Keyword And Return Status Should Be String ${suite or name}
- ${suite} = Run Keyword If ${is string} Get Test Suite ${suite or name}
- ... ELSE Set Variable ${suite or name}
- Should Be Equal ${suite.status} ${status} Wrong suite status
+ TRY
+ Should Be String ${suite or name}
+ EXCEPT
+ VAR ${suite} ${suite or name}
+ ELSE
+ ${suite} = Get Test Suite ${suite or name}
+ END
+ Should Be Equal ${suite.status} ${status} Wrong suite status
Should Be Equal ${suite.full_message} ${message} Wrong suite message
Should Contain Tests ${suite} @{tests}
@@ -172,3 +184,9 @@ Output should contain teardown error
[Arguments] ${error}
${keywords} = Get Elements ${OUTFILE} suite/kw
Element Text Should Be ${keywords[-1]} ${error} xpath=status
+
+JSON output should contain teardown error
+ [Arguments] ${error}
+ ${path} = Normalize Path ${OUTDIR}/output.json
+ ${data} = Evaluate json.load(open($path, 'rb'))
+ Should Be Equal ${data}[suite][teardown][message] ${error}
diff --git a/atest/robot/core/test_suite_init_file.robot b/atest/robot/core/test_suite_init_file.robot
index 59d27cef032..fa952fba835 100644
--- a/atest/robot/core/test_suite_init_file.robot
+++ b/atest/robot/core/test_suite_init_file.robot
@@ -21,14 +21,14 @@ Suite Documentation
Suite Setup
[Documentation] Setting and not setting setup using suite file
- Check Log Message ${suite.setup.kws[0].msgs[0]} Setup of test suite directory
+ Check Log Message ${suite.setup[0, 0]} Setup of test suite directory
Setup Should Not Be Defined ${subsuite_with_init}
Setup Should Not Be Defined ${subsuite_without_init}
Suite Teardown
[Documentation] Setting and not setting teardown using suite file
- Check Log Message ${suite.teardown.kws[1].msgs[0]} Teardown of test suite directory
- Check Log Message ${subsuite_with_init.teardown.kws[1].msgs[0]} Teardown of sub test suite directory
+ Check Log Message ${suite.teardown[1, 0]} Teardown of test suite directory
+ Check Log Message ${subsuite_with_init.teardown[1, 0]} Teardown of sub test suite directory
Teardown Should Not Be Defined ${subsuite_without_init}
Invalid Suite Setting
diff --git a/atest/robot/keywords/dots_in_keyword_name.robot b/atest/robot/keywords/dots_in_keyword_name.robot
index ac30e4bf2c8..1ea6962e9a1 100644
--- a/atest/robot/keywords/dots_in_keyword_name.robot
+++ b/atest/robot/keywords/dots_in_keyword_name.robot
@@ -44,5 +44,5 @@ Dots in library name and keyword name with full name
Conflicting names with dots
${tc} = Check Test Case ${TESTNAME}
- Check log message ${tc.kws[0].msgs[0]} Running keyword 'Conflict'.
- Check log message ${tc.kws[1].msgs[0]} Executing keyword 'In.name.conflict'.
+ Check log message ${tc[0, 0]} Running keyword 'Conflict'.
+ Check log message ${tc[1, 0]} Executing keyword 'In.name.conflict'.
diff --git a/atest/robot/keywords/duplicate_dynamic_keywords.robot b/atest/robot/keywords/duplicate_dynamic_keywords.robot
index 143869d3102..15e1a07854b 100644
--- a/atest/robot/keywords/duplicate_dynamic_keywords.robot
+++ b/atest/robot/keywords/duplicate_dynamic_keywords.robot
@@ -5,14 +5,14 @@ Resource atest_resource.robot
*** Test Cases ***
Using keyword defined multiple times fails
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.kws[0].full_name} DupeDynamicKeywords.Defined Twice
+ Should Be Equal ${tc[0].full_name} DupeDynamicKeywords.Defined Twice
Error in library DupeDynamicKeywords
... Adding keyword 'DEFINED TWICE' failed:
... Keyword with same name defined multiple times.
Keyword with embedded arguments defined multiple times fails at run-time
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.kws[0].full_name} Embedded twice
+ Should Be Equal ${tc[0].full_name} Embedded twice
Length Should Be ${ERRORS} 1
Exact duplicate is accepted
diff --git a/atest/robot/keywords/duplicate_hybrid_keywords.robot b/atest/robot/keywords/duplicate_hybrid_keywords.robot
index 5e86f8cfffe..3269b524a3c 100644
--- a/atest/robot/keywords/duplicate_hybrid_keywords.robot
+++ b/atest/robot/keywords/duplicate_hybrid_keywords.robot
@@ -5,14 +5,14 @@ Resource atest_resource.robot
*** Test Cases ***
Using keyword defined multiple times fails
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.kws[0].full_name} DupeHybridKeywords.Defined Twice
+ Should Be Equal ${tc[0].full_name} DupeHybridKeywords.Defined Twice
Error in library DupeHybridKeywords
... Adding keyword 'DEFINED TWICE' failed:
... Keyword with same name defined multiple times.
Keyword with embedded arguments defined multiple times fails at run-time
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.kws[0].full_name} Embedded twice
+ Should Be Equal ${tc[0].full_name} Embedded twice
Length Should Be ${ERRORS} 1
Exact duplicate is accepted
diff --git a/atest/robot/keywords/duplicate_static_keywords.robot b/atest/robot/keywords/duplicate_static_keywords.robot
index c712e80e7d1..c2d9ce15ff7 100644
--- a/atest/robot/keywords/duplicate_static_keywords.robot
+++ b/atest/robot/keywords/duplicate_static_keywords.robot
@@ -5,20 +5,20 @@ Resource atest_resource.robot
*** Test Cases ***
Using keyword defined twice fails
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.kws[0].full_name} DupeKeywords.Defined Twice
+ Should Be Equal ${tc[0].full_name} DupeKeywords.Defined Twice
Creating keyword should have failed 2 Defined twice
Using keyword defined thrice fails as well
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.kws[0].full_name} DupeKeywords.Defined Thrice
+ Should Be Equal ${tc[0].full_name} DupeKeywords.Defined Thrice
Creating keyword should have failed 0 Defined Thrice
Creating keyword should have failed 1 Defined Thrice
Keyword with embedded arguments defined twice fails at run-time
${tc} = Check Test Case ${TESTNAME}: Called with embedded args
- Should Be Equal ${tc.kws[0].full_name} Embedded arguments twice
+ Should Be Equal ${tc[0].full_name} Embedded arguments twice
${tc} = Check Test Case ${TESTNAME}: Called with exact name
- Should Be Equal ${tc.kws[0].full_name} Embedded \${arguments match} twice
+ Should Be Equal ${tc[0].full_name} Embedded \${arguments match} twice
Length Should Be ${ERRORS} 3
*** Keywords ***
diff --git a/atest/robot/keywords/embedded_arguments.robot b/atest/robot/keywords/embedded_arguments.robot
index 1540be432a8..328e5a43a4a 100644
--- a/atest/robot/keywords/embedded_arguments.robot
+++ b/atest/robot/keywords/embedded_arguments.robot
@@ -5,62 +5,64 @@ Resource atest_resource.robot
*** Test Cases ***
Embedded Arguments In User Keyword Name
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} This is always executed
- Check Keyword Data ${tc.kws[0]} User Peke Selects Advanced Python From Webshop \${name}, \${book}
- Check Log Message ${tc.kws[2].kws[0].msgs[0]} This is always executed
- Check Keyword Data ${tc.kws[2]} User Juha Selects Playboy From Webshop \${name}, \${book}
- File Should Contain ${OUTFILE}
- ... name="User Peke Selects Advanced Python From Webshop"
- File Should Contain ${OUTFILE}
- ... source_name="User \${user} Selects \${item} From Webshop"
- File Should Not Contain ${OUTFILE} source_name="Log"
+ Check Log Message ${tc[0, 0, 0]} This is always executed
+ Check Keyword Data ${tc[0]} User Peke Selects Advanced Python From Webshop \${name}, \${book}
+ Check Log Message ${tc[2, 0, 0]} This is always executed
+ Check Keyword Data ${tc[2]} User Juha Selects Playboy From Webshop \${name}, \${book}
+ File Should Contain ${OUTFILE} name="User Peke Selects Advanced Python From Webshop"
+ File Should Contain ${OUTFILE} source_name="User \${user} Selects \${item} From Webshop"
+ File Should Not Contain ${OUTFILE} source_name="Log"
+
+Embedded arguments with type conversion
+ [Documentation] This is tested more thorougly in 'variables/variable_types.robot'.
+ Check Test Case ${TEST NAME}
Complex Embedded Arguments
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} feature-works
- Check Log Message ${tc.kws[1].kws[0].msgs[0]} test case-is *executed*
- Check Log Message ${tc.kws[2].kws[0].msgs[0]} issue-is about to be done!
- File Should Contain ${OUTFILE} source_name="\${prefix:Given|When|Then} this
- File Should Not Contain ${OUTFILE} source_name="Log"
+ Check Log Message ${tc[0, 0, 0]} feature-works
+ Check Log Message ${tc[1, 0, 0]} test case-is *executed*
+ Check Log Message ${tc[2, 0, 0]} issue-is about to be done!
+ File Should Contain ${OUTFILE} source_name="\${prefix:Given|When|Then} this
+ File Should Not Contain ${OUTFILE} source_name="Log"
Embedded Arguments with BDD Prefixes
${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.kws[0]} Given user x selects y from webshop
- Check Keyword Data ${tc.kws[1]} When user x selects y from webshop
- Check Keyword Data ${tc.kws[2]} Then user x selects y from webshop \${x}, \${y}
- File Should Contain ${OUTFILE}
- ... name="Given user x selects y from webshop"
- File Should Contain ${OUTFILE}
- ... source_name="User \${user} Selects \${item} From Webshop"
+ Check Keyword Data ${tc[0]} Given user x selects y from webshop
+ Check Keyword Data ${tc[1]} When user x selects y from webshop
+ Check Keyword Data ${tc[2]} Then user x selects y from webshop \${x}, \${y}
+ File Should Contain ${OUTFILE} name="Given user x selects y from webshop"
+ File Should Contain ${OUTFILE} source_name="User \${user} Selects \${item} From Webshop"
File Should Not Contain ${OUTFILE} source_name="Log"
Argument Namespaces with Embedded Arguments
Check Test Case ${TEST NAME}
- File Should Contain ${OUTFILE} name="My embedded warrior"
- File Should Contain ${OUTFILE} source_name="My embedded \${var}"
+ File Should Contain ${OUTFILE} name="My embedded warrior"
+ File Should Contain ${OUTFILE} source_name="My embedded \${var}"
File Should Not Contain ${OUTFILE} source_name="Log"
Embedded Arguments as Variables
${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.kws[0]} User \${42} Selects \${EMPTY} From Webshop \${name}, \${item}
- Check Keyword Data ${tc.kws[2]} User \${name} Selects \${SPACE * 10} From Webshop \${name}, \${item}
- File Should Contain ${OUTFILE}
- ... name="User \${42} Selects \${EMPTY} From Webshop"
- File Should Contain ${OUTFILE}
- ... source_name="User \${user} Selects \${item} From Webshop"
- File Should Contain ${OUTFILE}
- ... name="User \${name} Selects \${SPACE * 10} From Webshop"
- File Should Contain ${OUTFILE}
- ... source_name="User \${user} Selects \${item} From Webshop"
+ Check Keyword Data ${tc[0]} User \${42} Selects \${EMPTY} From Webshop \${name}, \${item}
+ Check Keyword Data ${tc[2]} User \${name} Selects \${SPACE * 100}[:10] From Webshop \${name}, \${item}
+ File Should Contain ${OUTFILE} name="User \${42} Selects \${EMPTY} From Webshop"
+ File Should Contain ${OUTFILE} name="User \${name} Selects \${SPACE * 100}[:10] From Webshop"
+ File Should Contain ${OUTFILE} source_name="User \${user} Selects \${item} From Webshop"
File Should Not Contain ${OUTFILE} source_name="Log">
+Embedded arguments as variables and other content
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Keyword Data ${tc[0]} User \${foo}\${EMPTY}\${bar} Selects \${foo}, \${bar} and \${zap} From Webshop \${name}, \${item}
+
+Embedded arguments as variables containing characters that exist also in keyword name
+ Check Test Case ${TEST NAME}
+
Embedded Arguments as List And Dict Variables
${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.kws[1]} User \@{i1} Selects \&{i2} From Webshop \${o1}, \${o2}
+ Check Keyword Data ${tc[1]} User \@{i1} Selects \&{i2} From Webshop \${o1}, \${o2}
Non-Existing Variable in Embedded Arguments
${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.kws[0]} User \${non existing} Selects \${variables} From Webshop status=FAIL
+ Check Keyword Data ${tc[0]} User \${non existing} Selects \${variables} From Webshop status=FAIL
Invalid List Variable as Embedded Argument
Check Test Case ${TEST NAME}
@@ -86,47 +88,50 @@ Custom Regexp With Escape Chars
Grouping Custom Regexp
Check Test Case ${TEST NAME}
+Custom Regex With Leading And Trailing Spaces
+ Check Test Case ${TEST NAME}
+
Custom Regexp Matching Variables
Check Test Case ${TEST NAME}
+Custom regexp with inline Python evaluation
+ Check Test Case ${TEST NAME}
+
Non Matching Variable Is Accepted With Custom Regexp (But Not For Long)
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.body[0].msgs[0]}
+ Check Log Message ${tc[0, 0]}
... Embedded argument 'x' got value 'foo' that does not match custom pattern 'bar'. The argument is still accepted, but this behavior will change in Robot Framework 8.0. WARN
Partially Matching Variable Is Accepted With Custom Regexp (But Not For Long)
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.body[0].msgs[0]}
+ Check Log Message ${tc[0, 0]}
... Embedded argument 'x' got value 'ba' that does not match custom pattern 'bar'. The argument is still accepted, but this behavior will change in Robot Framework 8.0. WARN
- Check Log Message ${tc.body[0].msgs[1]}
+ Check Log Message ${tc[0, 1]}
... Embedded argument 'y' got value 'zapzap' that does not match custom pattern '...'. The argument is still accepted, but this behavior will change in Robot Framework 8.0. WARN
Non String Variable Is Accepted With Custom Regexp
Check Test Case ${TEST NAME}
-Regexp Extensions Are Not Supported
+Custom regexp with inline flag
Check Test Case ${TEST NAME}
- Creating Keyword Failed 0 292
- ... Regexp extensions like \${x:(?x)re} are not supported
- ... Regexp extensions are not allowed in embedded arguments.
Invalid Custom Regexp
Check Test Case ${TEST NAME}
- Creating Keyword Failed 1 295
+ Creating Keyword Failed 0 350
... Invalid \${x:(} Regexp
... Compiling embedded arguments regexp failed: *
Escaping Values Given As Embedded Arguments
${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.kws[0]} User \\\${nonex} Selects \\\\ From Webshop \${name}, \${item}
- Check Keyword Data ${tc.kws[2]} User \\ Selects \\ \\ From Webshop \${name}, \${item}
+ Check Keyword Data ${tc[0]} User \\\${nonex} Selects \\\\ From Webshop \${name}, \${item}
+ Check Keyword Data ${tc[2]} User \\ Selects \\ \\ From Webshop \${name}, \${item}
Embedded Arguments Syntax Is Case Insensitive
${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.kws[0]} x Gets y From The z
- Check Keyword Data ${tc.kws[1]} x gets y from the z
- Check Keyword Data ${tc.kws[2]} x GETS y from the z
- Check Keyword Data ${tc.kws[3]} x gets y FROM THE z
+ Check Keyword Data ${tc[0]} x Gets y From The z
+ Check Keyword Data ${tc[1]} x gets y from the z
+ Check Keyword Data ${tc[2]} x GETS y from the z
+ Check Keyword Data ${tc[3]} x gets y FROM THE z
Embedded Arguments Syntax is Space Sensitive
Check Test Case ${TEST NAME}
@@ -136,11 +141,11 @@ Embedded Arguments Syntax is Underscore Sensitive
Embedded Arguments In Resource File
${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.kws[0]} embedded_args_in_uk_1.Juha Uses Resource File \${ret}
+ Check Keyword Data ${tc[0]} embedded_args_in_uk_1.Juha Uses Resource File \${ret}
Embedded Arguments In Resource File Used Explicitly
${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.kws[0]} embedded_args_in_uk_1.peke uses resource file \${ret}
+ Check Keyword Data ${tc[0]} embedded_args_in_uk_1.peke uses resource file \${ret}
Keyword with only embedded arguments doesn't accept normal arguments
Check Test Case ${TEST NAME}
@@ -150,37 +155,37 @@ Keyword with embedded args cannot be used as "normal" keyword
Keyword with both embedded and normal arguments
${tc} = Check Test Case ${TEST NAME}
- Check Log message ${tc.body[0].body[0].msgs[0]} 2 horses are walking
- Check Log message ${tc.body[1].body[0].msgs[0]} 2 horses are swimming
- Check Log message ${tc.body[2].body[0].msgs[0]} 3 dogs are walking
+ Check Log message ${tc[0, 0, 0]} 2 horses are walking
+ Check Log message ${tc[1, 0, 0]} 2 horses are swimming
+ Check Log message ${tc[2, 0, 0]} 3 dogs are walking
Keyword with both embedded and normal arguments with too few arguments
Check Test Case ${TEST NAME}
Keyword matching multiple keywords in test case file
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} foo+tc+bar
- Check Log Message ${tc.kws[1].kws[0].msgs[0]} foo-tc-bar
- Check Log Message ${tc.kws[2].kws[0].msgs[0]} foo+tc+bar+tc+zap
+ Check Log Message ${tc[0, 0, 0]} foo+tc+bar
+ Check Log Message ${tc[1, 0, 0]} foo-tc-bar
+ Check Log Message ${tc[2, 0, 0]} foo+tc+bar+tc+zap
Keyword matching multiple keywords in one resource file
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} foo+r1+bar
- Check Log Message ${tc.kws[1].kws[0].msgs[0]} foo-r1-bar
+ Check Log Message ${tc[0, 0, 0]} foo+r1+bar
+ Check Log Message ${tc[1, 0, 0]} foo-r1-bar
Keyword matching multiple keywords in different resource files
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} foo-r1-bar
- Check Log Message ${tc.kws[1].kws[0].msgs[0]} foo-r2-bar
+ Check Log Message ${tc[0, 0, 0]} foo-r1-bar
+ Check Log Message ${tc[1, 0, 0]} foo-r2-bar
Keyword matching multiple keywords in one and different resource files
Check Test Case ${TEST NAME}
Same name with different regexp works
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} a car
- Check Log Message ${tc.kws[1].kws[0].msgs[0]} a dog
- Check Log Message ${tc.kws[2].kws[0].msgs[0]} a cow
+ Check Log Message ${tc[0, 0, 0]} a car
+ Check Log Message ${tc[1, 0, 0]} a dog
+ Check Log Message ${tc[2, 0, 0]} a cow
Same name with different regexp matching multiple fails
Check Test Case ${TEST NAME}
diff --git a/atest/robot/keywords/embedded_arguments_library_keywords.robot b/atest/robot/keywords/embedded_arguments_library_keywords.robot
index b0646a0c6e1..67da8ca77ce 100755
--- a/atest/robot/keywords/embedded_arguments_library_keywords.robot
+++ b/atest/robot/keywords/embedded_arguments_library_keywords.robot
@@ -5,66 +5,69 @@ Resource atest_resource.robot
*** Test Cases ***
Embedded Arguments In Library Keyword Name
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} This is always executed
- Check Keyword Data ${tc.kws[0]} embedded_args_in_lk_1.User Peke Selects Advanced Python From Webshop \${name}, \${book}
- Check Log Message ${tc.kws[2].msgs[0]} This is always executed
- Check Keyword Data ${tc.kws[2]} embedded_args_in_lk_1.User Juha selects Playboy from webshop \${name}, \${book}
- File Should Contain ${OUTFILE}
- ... name="User Peke Selects Advanced Python From Webshop"
- File Should Contain ${OUTFILE}
- ... owner="embedded_args_in_lk_1"
- File Should Contain ${OUTFILE}
- ... source_name="User \${user} Selects \${item} From Webshop"
- File Should Not Contain ${OUTFILE} source_name="Log"
+ Check Log Message ${tc[0, 0]} This is always executed
+ Check Keyword Data ${tc[0]} embedded_args_in_lk_1.User Peke Selects Advanced Python From Webshop \${name}, \${book}
+ Check Log Message ${tc[2, 0]} This is always executed
+ Check Keyword Data ${tc[2]} embedded_args_in_lk_1.User Juha selects Playboy from webshop \${name}, \${book}
+ File Should Contain ${OUTFILE} name="User Peke Selects Advanced Python From Webshop"
+ File Should Contain ${OUTFILE} owner="embedded_args_in_lk_1"
+ File Should Contain ${OUTFILE} source_name="User \${user} Selects \${item} From Webshop"
+ File Should Not Contain ${OUTFILE} source_name="Log"
Complex Embedded Arguments
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} feature-works
- Check Log Message ${tc.kws[1].msgs[0]} test case-is *executed*
- Check Log Message ${tc.kws[2].msgs[0]} issue-is about to be done!
- File Should Contain ${OUTFILE} source_name="\${prefix:Given|When|Then} this
- File Should Not Contain ${OUTFILE} source_name="Log"
+ Check Log Message ${tc[0, 0]} feature-works
+ Check Log Message ${tc[1, 0]} test case-is *executed*
+ Check Log Message ${tc[2, 0]} issue-is about to be done!
+ File Should Contain ${OUTFILE} source_name="\${prefix:Given|When|Then} this
+ File Should Not Contain ${OUTFILE} source_name="Log"
Embedded Arguments with BDD Prefixes
${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.kws[0]} embedded_args_in_lk_1.Given user x selects y from webshop
- Check Keyword Data ${tc.kws[1]} embedded_args_in_lk_1.When user x selects y from webshop
- Check Keyword Data ${tc.kws[2]} embedded_args_in_lk_1.Then user x selects y from webshop \${x}, \${y}
- File Should Contain ${OUTFILE} name="Given user x selects y from webshop"
- File Should Contain ${OUTFILE} owner="embedded_args_in_lk_1"
- File Should Contain ${OUTFILE} source_name="User \${user} Selects \${item} From Webshop"
+ Check Keyword Data ${tc[0]} embedded_args_in_lk_1.Given user x selects y from webshop
+ Check Keyword Data ${tc[1]} embedded_args_in_lk_1.When user x selects y from webshop
+ Check Keyword Data ${tc[2]} embedded_args_in_lk_1.Then user x selects y from webshop \${x}, \${y}
+ File Should Contain ${OUTFILE} name="Given user x selects y from webshop"
+ File Should Contain ${OUTFILE} owner="embedded_args_in_lk_1"
+ File Should Contain ${OUTFILE} source_name="User \${user} Selects \${item} From Webshop"
File Should Not Contain ${OUTFILE} source_name="Log"
Argument Namespaces with Embedded Arguments
Check Test Case ${TEST NAME}
- File Should Contain ${OUTFILE} name="My embedded warrior"
- File Should Contain ${OUTFILE} owner="embedded_args_in_lk_1"
- File Should Contain ${OUTFILE} source_name="My embedded \${var}"
+ File Should Contain ${OUTFILE} name="My embedded warrior"
+ File Should Contain ${OUTFILE} owner="embedded_args_in_lk_1"
+ File Should Contain ${OUTFILE} source_name="My embedded \${var}"
File Should Not Contain ${OUTFILE} source_name="Log"
Embedded Arguments as Variables
${tc} = Check Test Case ${TEST NAME}
- File Should Contain ${OUTFILE}
- ... name="User \${42} Selects \${EMPTY} From Webshop"
- File Should Contain ${OUTFILE}
- ... owner="embedded_args_in_lk_1"
- File Should Contain ${OUTFILE}
- ... source_name="User \${user} Selects \${item} From Webshop"
- File Should Contain ${OUTFILE}
- ... name="User \${name} Selects \${SPACE * 10} From Webshop"
+ File Should Contain ${OUTFILE} name="User \${42} Selects \${EMPTY} From Webshop"
+ File Should Contain ${OUTFILE} owner="embedded_args_in_lk_1"
+ File Should Contain ${OUTFILE} source_name="User \${user} Selects \${item} From Webshop"
+ File Should Contain ${OUTFILE} name="User \${name} Selects \${SPACE * 100}[:10] From Webshop"
File Should Not Contain ${OUTFILE} source_name="Log"
+Embedded arguments as variables and other content
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Keyword Data ${tc[0]} embedded_args_in_lk_1.User \${foo}\${EMPTY}\${bar} Selects \${foo}, \${bar} and \${zap} From Webshop \${name}, \${item}
+
+Embedded arguments as variables containing characters in keyword name
+ Check Test Case ${TEST NAME}
+
Embedded Arguments as List And Dict Variables
${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.kws[1]} embedded_args_in_lk_1.User \@{inp1} Selects \&{inp2} From Webshop \${out1}, \${out2}
+ Check Keyword Data ${tc[1]} embedded_args_in_lk_1.User \@{inp1} Selects \&{inp2} From Webshop \${out1}, \${out2}
Non-Existing Variable in Embedded Arguments
${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.kws[0]} embedded_args_in_lk_1.User \${non existing} Selects \${variables} From Webshop status=FAIL
+ Check Keyword Data ${tc[0]} embedded_args_in_lk_1.User \${non existing} Selects \${variables} From Webshop status=FAIL
Custom Embedded Argument Regexp
Check Test Case ${TEST NAME}
+Custom regexp with inline flags
+ Check Test Case ${TEST NAME}
+
Custom Regexp With Curly Braces
Check Test Case ${TEST NAME}
@@ -77,16 +80,19 @@ Grouping Custom Regexp
Custom Regexp Matching Variables
Check Test Case ${TEST NAME}
+Custom regexp with inline Python evaluation
+ Check Test Case ${TEST NAME}
+
Non Matching Variable Is Accepted With Custom Regexp (But Not For Long)
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.body[0].msgs[0]}
+ Check Log Message ${tc[0, 0]}
... Embedded argument 'x' got value 'foo' that does not match custom pattern 'bar'. The argument is still accepted, but this behavior will change in Robot Framework 8.0. WARN
Partially Matching Variable Is Accepted With Custom Regexp (But Not For Long)
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.body[0].msgs[0]}
+ Check Log Message ${tc[0, 0]}
... Embedded argument 'x' got value 'ba' that does not match custom pattern 'bar'. The argument is still accepted, but this behavior will change in Robot Framework 8.0. WARN
- Check Log Message ${tc.body[0].msgs[1]}
+ Check Log Message ${tc[0, 1]}
... Embedded argument 'y' got value 'zapzap' that does not match custom pattern '...'. The argument is still accepted, but this behavior will change in Robot Framework 8.0. WARN
Non String Variable Is Accepted With Custom Regexp
@@ -100,9 +106,9 @@ Embedded Arguments Syntax is Underscore Sensitive
Keyword matching multiple keywords in library file
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} foo+lib+bar
- Check Log Message ${tc.kws[1].msgs[0]} foo-lib-bar
- Check Log Message ${tc.kws[2].msgs[0]} foo+lib+bar+lib+zap
+ Check Log Message ${tc[0, 0]} foo+lib+bar
+ Check Log Message ${tc[1, 0]} foo-lib-bar
+ Check Log Message ${tc[2, 0]} foo+lib+bar+lib+zap
Keyword matching multiple keywords in different library files
Check Test Case ${TEST NAME}
@@ -115,9 +121,9 @@ Keyword with embedded args cannot be used as "normal" keyword
Keyword with both embedded and normal arguments
${tc} = Check Test Case ${TEST NAME}
- Check Log message ${tc.body[0].msgs[0]} 2 horses are walking
- Check Log message ${tc.body[1].msgs[0]} 2 horses are swimming
- Check Log message ${tc.body[2].msgs[0]} 3 dogs are walking
+ Check Log message ${tc[0, 0]} 2 horses are walking
+ Check Log message ${tc[1, 0]} 2 horses are swimming
+ Check Log message ${tc[2, 0]} 3 dogs are walking
Conversion with embedded and normal arguments
Check Test Case ${TEST NAME}
@@ -130,6 +136,7 @@ Must accept at least as many positional arguments as there are embedded argument
Error in library embedded_args_in_lk_1
... Adding keyword 'Wrong \${number} of embedded \${args}' failed:
... Keyword must accept at least as many positional arguments as it has embedded arguments.
+ ... index=2
Optional Non-Embedded Args Are Okay
Check Test Case ${TESTNAME}
@@ -142,12 +149,27 @@ Lists are not expanded when keyword accepts varargs
Same name with different regexp works
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} a car
- Check Log Message ${tc.kws[1].msgs[0]} a dog
- Check Log Message ${tc.kws[2].msgs[0]} a cow
+ Check Log Message ${tc[0, 0]} a car
+ Check Log Message ${tc[1, 0]} a dog
+ Check Log Message ${tc[2, 0]} a cow
Same name with different regexp matching multiple fails
Check Test Case ${TEST NAME}
Same name with same regexp fails
Check Test Case ${TEST NAME}
+
+Embedded arguments cannot have type information
+ Check Test Case ${TEST NAME}
+ Error in library embedded_args_in_lk_1
+ ... Adding keyword 'Embedded \${arg: int} with type is not supported' failed:
+ ... Library keywords do not support type information with embedded arguments like '\${arg: int}'.
+ ... Use type hints with function arguments instead.
+ ... index=1
+
+Embedded type can nevertheless be invalid
+ Check Test Case ${TEST NAME}
+ Error in library embedded_args_in_lk_1
+ ... Adding keyword 'embedded_types_can_be_invalid' failed:
+ ... Invalid embedded argument '\${invalid: bad}': Unrecognized type 'bad'.
+ ... index=0
diff --git a/atest/robot/keywords/keyword_documentation.robot b/atest/robot/keywords/keyword_documentation.robot
index 8cc336b170e..2ae9f37eeba 100644
--- a/atest/robot/keywords/keyword_documentation.robot
+++ b/atest/robot/keywords/keyword_documentation.robot
@@ -26,4 +26,4 @@ Multiline documentation with split short doc
Verify Documentation
[Arguments] ${doc} ${test}=${TEST NAME}
${tc} = Check Test Case ${test}
- Should Be Equal ${tc.kws[0].doc} ${doc}
+ Should Be Equal ${tc[0].doc} ${doc}
diff --git a/atest/robot/keywords/keyword_names.robot b/atest/robot/keywords/keyword_names.robot
index 88e2ddc7c6a..dccf6c6afa7 100644
--- a/atest/robot/keywords/keyword_names.robot
+++ b/atest/robot/keywords/keyword_names.robot
@@ -20,38 +20,38 @@ Base Keyword Names In Test Case
Test Case File User Keyword Names In Test Case File User Keyword
${test} = Check Test Case Test Case File User Keyword Names In Test Case File User Keyword
- Check Name and Three Keyword Names ${test.body[0]} Using Test Case File User Keywords Keyword Only In Test Case File
- Should Be Equal ${test.body[1].full_name} Using Test Case File User Keywords Nested
- Check Name and Three Keyword Names ${test.body[1].body[0]} Using Test Case File User Keywords Keyword Only In Test Case File
- Check Name and Three Keyword Names ${test.body[1].body[1]} Using Test Case File User Keywords Keyword Only In Test Case File
+ Check Name and Three Keyword Names ${test[0]} Using Test Case File User Keywords Keyword Only In Test Case File
+ Should Be Equal ${test[1].full_name} Using Test Case File User Keywords Nested
+ Check Name and Three Keyword Names ${test[1, 0]} Using Test Case File User Keywords Keyword Only In Test Case File
+ Check Name and Three Keyword Names ${test[1, 1]} Using Test Case File User Keywords Keyword Only In Test Case File
Resource File User Keyword Names In Test Case File User Keyword
${test} = Check Test Case Resource File User Keyword Names In Test Case File User Keyword
- Check Name and Three Keyword Names ${test.body[0]} Using Resource File User Keywords my_resource_1.Keyword Only In Resource 1
- Should Be Equal ${test.body[1].full_name} Using Resource File User Keywords Nested
- Check Name and Three Keyword Names ${test.body[1].body[0]} Using Resource File User Keywords my_resource_1.Keyword Only In Resource 1
- Check Name and Three Keyword Names ${test.body[1].body[1]} Using Resource File User Keywords my_resource_1.Keyword Only In Resource 1
+ Check Name and Three Keyword Names ${test[0]} Using Resource File User Keywords my_resource_1.Keyword Only In Resource 1
+ Should Be Equal ${test[1].full_name} Using Resource File User Keywords Nested
+ Check Name and Three Keyword Names ${test[1, 0]} Using Resource File User Keywords my_resource_1.Keyword Only In Resource 1
+ Check Name and Three Keyword Names ${test[1, 1]} Using Resource File User Keywords my_resource_1.Keyword Only In Resource 1
Base Keyword Names In Test Case File User Keyword
${test} = Check Test Case Base Keyword Names In Test Case File User Keyword
- Check Name and Three Keyword Names ${test.body[0]} Using Base Keywords MyLibrary1.Keyword Only In Library 1
- Should Be Equal ${test.body[1].full_name} Using Base Keywords Nested
- Check Name and Three Keyword Names ${test.body[1].body[0]} Using Base Keywords MyLibrary1.Keyword Only In Library 1
- Check Name and Three Keyword Names ${test.body[1].body[1]} Using Base Keywords MyLibrary1.Keyword Only In Library 1
+ Check Name and Three Keyword Names ${test[0]} Using Base Keywords MyLibrary1.Keyword Only In Library 1
+ Should Be Equal ${test[1].full_name} Using Base Keywords Nested
+ Check Name and Three Keyword Names ${test[1, 0]} Using Base Keywords MyLibrary1.Keyword Only In Library 1
+ Check Name and Three Keyword Names ${test[1, 1]} Using Base Keywords MyLibrary1.Keyword Only In Library 1
Test Case File User Keyword Names In Resource File User Keyword
${test} = Check Test Case Test Case File User Keyword Names In Resource File User Keyword
- Should Be Equal ${test.body[0].full_name} my_resource_1.Using Test Case File User Keywords In Resource
- Check Name and Three Keyword Names ${test.body[0].body[0]} Using Test Case File User Keywords Keyword Only In Test Case File
+ Should Be Equal ${test[0].full_name} my_resource_1.Using Test Case File User Keywords In Resource
+ Check Name and Three Keyword Names ${test[0, 0]} Using Test Case File User Keywords Keyword Only In Test Case File
Resource File User Keyword Names In Resource File User Keyword
${test} = Check Test Case Resource File User Keyword Names In Resource File User Keyword
- Check Name and Three Keyword Names ${test.body[0]} my_resource_1.Using Resource File User Keywords In Resource 1 my_resource_1.Keyword Only In Resource 1
- Check Name and Three Keyword Names ${test.body[1]} my_resource_1.Using Resource File User Keywords In Resource 2 my_resource_2.Keyword Only In Resource 2
+ Check Name and Three Keyword Names ${test[0]} my_resource_1.Using Resource File User Keywords In Resource 1 my_resource_1.Keyword Only In Resource 1
+ Check Name and Three Keyword Names ${test[1]} my_resource_1.Using Resource File User Keywords In Resource 2 my_resource_2.Keyword Only In Resource 2
Base Keyword Names In Resource File User Keyword
${test} = Check Test Case Base Keyword Names In Resource File User Keyword
- Check Name and Three Keyword Names ${test.body[0]} my_resource_1.Using Base Keywords In Resource MyLibrary1.Keyword Only In Library 1
+ Check Name and Three Keyword Names ${test[0]} my_resource_1.Using Base Keywords In Resource MyLibrary1.Keyword Only In Library 1
User Keyword Name Containing Dots
Check Test And Three Keyword Names User Keyword Name Containing Dots User Keyword.Name
@@ -61,47 +61,47 @@ User Keyword Name Ending With Dot
Name Set Using 'robot_name' Attribute
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.kws[0].full_name} MyLibrary1.Name set using 'robot_name' attribute
- Check Log Message ${tc.kws[0].msgs[0]} My name was set using 'robot_name' attribute!
+ Should Be Equal ${tc[0].full_name} MyLibrary1.Name set using 'robot_name' attribute
+ Check Log Message ${tc[0, 0]} My name was set using 'robot_name' attribute!
Name Set Using 'robot.api.deco.keyword' Decorator
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.kws[0].full_name} MyLibrary1.Name set using 'robot.api.deco.keyword' decorator
- Check Log Message ${tc.kws[0].msgs[0]} My name was set using 'robot.api.deco.keyword' decorator!
+ Should Be Equal ${tc[0].full_name} MyLibrary1.Name set using 'robot.api.deco.keyword' decorator
+ Check Log Message ${tc[0, 0]} My name was set using 'robot.api.deco.keyword' decorator!
Custom non-ASCII name
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.kws[0].full_name} MyLibrary1.Custom nön-ÄSCII name
+ Should Be Equal ${tc[0].full_name} MyLibrary1.Custom nön-ÄSCII name
Old Name Doesn't Work If Name Set Using 'robot_name'
Check Test Case ${TESTNAME}
Keyword can just be marked without changing its name
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.kws[0].full_name} MyLibrary1.No Custom Name Given 1
- Should Be Equal ${tc.kws[1].full_name} MyLibrary1.No Custom Name Given 2
+ Should Be Equal ${tc[0].full_name} MyLibrary1.No Custom Name Given 1
+ Should Be Equal ${tc[1].full_name} MyLibrary1.No Custom Name Given 2
Functions decorated with @keyword can start with underscrore
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.kws[0].full_name} MyLibrary1.I Start With An Underscore And I Am Ok
- Check Log Message ${tc.kws[0].msgs[0]} I'm marked with @keyword
- Should Be Equal ${tc.kws[1].full_name} MyLibrary1.Function name can be whatever
- Check Log Message ${tc.kws[1].msgs[0]} Real name set by @keyword
+ Should Be Equal ${tc[0].full_name} MyLibrary1.I Start With An Underscore And I Am Ok
+ Check Log Message ${tc[0, 0]} I'm marked with @keyword
+ Should Be Equal ${tc[1].full_name} MyLibrary1.Function name can be whatever
+ Check Log Message ${tc[1, 0]} Real name set by @keyword
Assignment is not part of name
${tc} = Check Test Case ${TESTNAME}
- Check Keyword Data ${tc.kws[0]} BuiltIn.Log args=No assignment
- Check Keyword Data ${tc.kws[1]} BuiltIn.Set Variable assign=\${var} args=value
- Check Keyword Data ${tc.kws[2]} BuiltIn.Set Variable assign=\${v1}, \${v2} args=1, 2
- Check Keyword Data ${tc.kws[3]} BuiltIn.Evaluate assign=\${first}, \@{rest} args=range(10)
+ Check Keyword Data ${tc[0]} BuiltIn.Log args=No assignment
+ Check Keyword Data ${tc[1]} BuiltIn.Set Variable assign=\${var} args=value
+ Check Keyword Data ${tc[2]} BuiltIn.Set Variable assign=\${v1}, \${v2} args=1, 2
+ Check Keyword Data ${tc[3]} BuiltIn.Evaluate assign=\${first}, \@{rest} args=range(10)
Library name and keyword name are separate
${tc} = Check Test Case ${TESTNAME}
- Keyword and library names should be ${tc.kws[0]} Keyword Only In Test Case File
- Keyword and library names should be ${tc.kws[1]} Keyword Only In Resource 1 my_resource_1
- Keyword and library names should be ${tc.kws[2]} Keyword Only In Resource 1 my_resource_1
- Keyword and library names should be ${tc.kws[3]} Log BuiltIn
- Keyword and library names should be ${tc.kws[4]} Log BuiltIn
+ Keyword and library names should be ${tc[0]} Keyword Only In Test Case File
+ Keyword and library names should be ${tc[1]} Keyword Only In Resource 1 my_resource_1
+ Keyword and library names should be ${tc[2]} Keyword Only In Resource 1 my_resource_1
+ Keyword and library names should be ${tc[3]} Log BuiltIn
+ Keyword and library names should be ${tc[4]} Log BuiltIn
Empty keyword name is not allowed
Error in library MyLibrary1
@@ -121,9 +121,9 @@ Check Name And Three Keyword Names
Check Three Keyword Names
[Arguments] ${item} ${exp_kw_name}
- Should Be Equal ${item.body[0].full_name} ${exp_kw_name}
- Should Be Equal ${item.body[1].full_name} ${exp_kw_name}
- Should Be Equal ${item.body[2].full_name} ${exp_kw_name}
+ Should Be Equal ${item[0].full_name} ${exp_kw_name}
+ Should Be Equal ${item[1].full_name} ${exp_kw_name}
+ Should Be Equal ${item[2].full_name} ${exp_kw_name}
Keyword and library names should be
[Arguments] ${kw} ${kwname} ${libname}=${None}
diff --git a/atest/robot/keywords/keyword_namespaces.robot b/atest/robot/keywords/keyword_namespaces.robot
index 0037600209b..b18f7f4fd36 100644
--- a/atest/robot/keywords/keyword_namespaces.robot
+++ b/atest/robot/keywords/keyword_namespaces.robot
@@ -31,39 +31,39 @@ Keyword From Test Case File Overriding Local Keyword In Resource File Is Depreca
... Keyword 'my_resource_1.Use test case file keyword even when local keyword with same name exists' called keyword
... 'Keyword Everywhere' that exists both in the same resource file as the caller and in the suite file using that
... resource. The keyword in the suite file is used now, but this will change in Robot Framework 8.0.
- Check Log Message ${tc.body[0].body[0].msgs[0]} ${message} WARN
+ Check Log Message ${tc[0, 0, 0]} ${message} WARN
Check Log Message ${ERRORS}[1] ${message} WARN
Local keyword in resource file has precedence over keywords in other resource files
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.body[0].body[0].body[0].msgs[0]} Keyword in resource 1
- Check Log Message ${tc.body[1].body[0].body[0].msgs[0]} Keyword in resource 2
+ Check Log Message ${tc[0, 0, 0, 0]} Keyword in resource 1
+ Check Log Message ${tc[1, 0, 0, 0]} Keyword in resource 2
Search order has precedence over local keyword in resource file
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.body[0].body[0].body[0].msgs[0]} Keyword in resource 1
- Check Log Message ${tc.body[1].body[0].body[0].msgs[0]} Keyword in resource 1
+ Check Log Message ${tc[0, 0, 0, 0]} Keyword in resource 1
+ Check Log Message ${tc[1, 0, 0, 0]} Keyword in resource 1
Keyword From Custom Library Overrides Keywords From Standard Library
${tc} = Check Test Case ${TEST NAME}
- Verify Override Message ${ERRORS}[2] ${tc.kws[0]} Comment BuiltIn
- Verify Override Message ${ERRORS}[3] ${tc.kws[1]} Copy Directory OperatingSystem
+ Verify Override Message ${ERRORS}[2] ${tc[0]} Comment BuiltIn
+ Verify Override Message ${ERRORS}[3] ${tc[1]} Copy Directory OperatingSystem
Search order can give presedence to standard library keyword over custom keyword
${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.kws[1]} BuiltIn.Comment args=Used from BuiltIn
- Verify Override Message ${ERRORS}[4] ${tc.kws[2]} Copy Directory OperatingSystem
+ Check Keyword Data ${tc[1]} BuiltIn.Comment args=Used from BuiltIn
+ Verify Override Message ${ERRORS}[4] ${tc[2]} Copy Directory OperatingSystem
Search order can give presedence to custom keyword over standard library keyword
${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.kws[1]} MyLibrary1.Comment
- Check Log Message ${tc.kws[1].msgs[0]} Overrides keyword from BuiltIn library
- Check Keyword Data ${tc.kws[2]} MyLibrary1.Copy Directory
- Check Log Message ${tc.kws[2].msgs[0]} Overrides keyword from OperatingSystem library
+ Check Keyword Data ${tc[1]} MyLibrary1.Comment
+ Check Log Message ${tc[1, 0]} Overrides keyword from BuiltIn library
+ Check Keyword Data ${tc[2]} MyLibrary1.Copy Directory
+ Check Log Message ${tc[2, 0]} Overrides keyword from OperatingSystem library
Keyword From Custom Library Overrides Keywords From Standard Library Even When Std Lib Imported With Different Name
${tc} = Check Test Case ${TEST NAME}
- Verify Override Message ${ERRORS}[5] ${tc.kws[0]} Replace String
+ Verify Override Message ${ERRORS}[5] ${tc[0]} Replace String
... String MyLibrary2 Std With Name My With Name
No Warning When Custom Library Keyword Is Registered As RunKeyword Variant And It Has Same Name As Std Keyword
@@ -76,10 +76,10 @@ Keyword In More Than One Custom Library And Standard Library
Keywords are first searched from test case file even if they contain dot
${tc} = Check Test Case ${TESTNAME}
- Check log message ${tc.kws[0].kws[0].msgs[0]} Keyword in test case file overriding keyword in my_resource_1
- Check log message ${tc.kws[0].kws[1].kws[0].msgs[0]} Keyword in resource 1
- Check log message ${tc.kws[1].kws[0].msgs[0]} Keyword in test case file overriding keyword in BuiltIn
- Check log message ${tc.kws[1].kws[1].msgs[0]} Using keyword in test case file here!
+ Check log message ${tc[0, 0, 0]} Keyword in test case file overriding keyword in my_resource_1
+ Check log message ${tc[0, 1, 0, 0]} Keyword in resource 1
+ Check log message ${tc[1, 0, 0]} Keyword in test case file overriding keyword in BuiltIn
+ Check log message ${tc[1, 1, 0]} Using keyword in test case file here!
*** Keywords ***
Verify override message
@@ -95,5 +95,5 @@ Verify override message
... To select explicitly, and to get rid of this warning, use either '${ctm long}.${name}'
... or '${std long}.${name}'.
Check Log Message ${error msg} ${expected} WARN
- Check Log Message ${kw.msgs[0]} ${expected} WARN
- Check Log Message ${kw.msgs[1]} Overrides keyword from ${standard} library
+ Check Log Message ${kw[0]} ${expected} WARN
+ Check Log Message ${kw[1]} Overrides keyword from ${standard} library
diff --git a/atest/robot/keywords/keywords_implemented_in_c.robot b/atest/robot/keywords/keywords_implemented_in_c.robot
index 0f44b4e807e..99bd99a9164 100644
--- a/atest/robot/keywords/keywords_implemented_in_c.robot
+++ b/atest/robot/keywords/keywords_implemented_in_c.robot
@@ -5,7 +5,7 @@ Resource atest_resource.robot
*** Test Cases ***
Use with correct arguments
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[-1].msgs[0]} This is a bit weird ...
+ Check Log Message ${tc[-1, 0]} This is a bit weird ...
Use with incorrect arguments
${error} = Set Variable If ${INTERPRETER.is_pypy} or ${INTERPRETER.version_info} >= (3, 7)
diff --git a/atest/robot/keywords/optional_given_when_then.robot b/atest/robot/keywords/optional_given_when_then.robot
index 9f8dd9f884f..4b1b88656f1 100644
--- a/atest/robot/keywords/optional_given_when_then.robot
+++ b/atest/robot/keywords/optional_given_when_then.robot
@@ -5,65 +5,87 @@ Resource atest_resource.robot
*** Test Cases ***
In user keyword name with normal arguments
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.kws[0].full_name} Given we don't drink too many beers
- Should Be Equal ${tc.kws[1].full_name} When we are in
- Should Be Equal ${tc.kws[2].full_name} But we don't drink too many beers
- Should Be Equal ${tc.kws[3].full_name} And time
- Should Be Equal ${tc.kws[4].full_name} Then we get this feature ready today
- Should Be Equal ${tc.kws[5].full_name} and we don't drink too many beers
+ Should Be Equal ${tc[0].full_name} Given we don't drink too many beers
+ Should Be Equal ${tc[1].full_name} When we are in
+ Should Be Equal ${tc[2].full_name} But we don't drink too many beers
+ Should Be Equal ${tc[3].full_name} And time
+ Should Be Equal ${tc[4].full_name} Then we get this feature ready today
+ Should Be Equal ${tc[5].full_name} and we don't drink too many beers
In user keyword name with embedded arguments
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.kws[0].full_name} Given we are in Berlin city
- Should Be Equal ${tc.kws[1].full_name} When it does not rain
- Should Be Equal ${tc.kws[2].full_name} And we get this feature implemented
- Should Be Equal ${tc.kws[3].full_name} Then we go to walking tour
- Should Be Equal ${tc.kws[4].full_name} but it does not rain
+ Should Be Equal ${tc[0].full_name} Given we are in Berlin city
+ Should Be Equal ${tc[1].full_name} When it does not rain
+ Should Be Equal ${tc[2].full_name} And we get this feature implemented
+ Should Be Equal ${tc[3].full_name} Then we go to walking tour
+ Should Be Equal ${tc[4].full_name} but it does not rain
In library keyword name
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.kws[0].full_name} BuiltIn.Given Should Be Equal
- Should Be Equal ${tc.kws[1].full_name} BuiltIn.And Should Not Match
- Should Be Equal ${tc.kws[2].full_name} BuiltIn.But Should Match
- Should Be Equal ${tc.kws[3].full_name} BuiltIn.When set test variable
- Should Be Equal ${tc.kws[4].full_name} BuiltIn.THEN should be equal
+ Should Be Equal ${tc[0].full_name} BuiltIn.Given Should Be Equal
+ Should Be Equal ${tc[1].full_name} BuiltIn.And Should Not Match
+ Should Be Equal ${tc[2].full_name} BuiltIn.But Should Match
+ Should Be Equal ${tc[3].full_name} BuiltIn.When set test variable
+ Should Be Equal ${tc[4].full_name} BuiltIn.THEN should be equal
In user keyword in resource file
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.kws[0].full_name} optional_given_when_then.Given Keyword Is In Resource File
- Should Be Equal ${tc.kws[1].full_name} optional_given_when_then.and another resource file
+ Should Be Equal ${tc[0].full_name} optional_given_when_then.Given Keyword Is In Resource File
+ Should Be Equal ${tc[1].full_name} optional_given_when_then.and another resource file
Correct Name Shown in Keyword Not Found Error
Check Test Case ${TEST NAME}
Keyword can be used with and without prefix
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.kws[0].full_name} GiveN we don't drink too many beers
- Should Be Equal ${tc.kws[1].full_name} and we don't drink too many beers
- Should Be Equal ${tc.kws[2].full_name} We don't drink too many beers
- Should Be Equal ${tc.kws[3].full_name} When time
- Should Be Equal ${tc.kws[4].full_name} Time
- Should Be Equal ${tc.kws[5].full_name} Then we are in Berlin city
- Should Be Equal ${tc.kws[6].full_name} we are in Berlin city
+ Should Be Equal ${tc[0].full_name} GiveN we don't drink too many beers
+ Should Be Equal ${tc[1].full_name} and we don't drink too many beers
+ Should Be Equal ${tc[2].full_name} We don't drink too many beers
+ Should Be Equal ${tc[3].full_name} When time
+ Should Be Equal ${tc[4].full_name} Time
+ Should Be Equal ${tc[5].full_name} Then we are in Berlin city
+ Should Be Equal ${tc[6].full_name} we are in Berlin city
+
+Only one prefix is processed
+ ${tc} = Check Test Case ${TEST NAME}
+ Should Be Equal ${tc[0].full_name} Given we are in Berlin city
+ Should Be Equal ${tc[1].full_name} but then we are in Berlin city
+
+First word of a keyword can be a prefix
+ ${tc} = Check Test Case ${TEST NAME}
+ Should Be Equal ${tc[0].full_name} Given the prefix is part of the keyword
+
+First word in a keyword can be an argument
+ ${tc} = Check Test Case ${TEST NAME}
+ Should Be Equal ${tc[0].full_name} Given we don't drink too many beers
+ Should Be Equal ${tc[1].full_name} Then Pekka drinks lonkero instead
+ Should Be Equal ${tc[2].full_name} and Miikka drinks water instead
+ Should Be Equal ${tc[3].full_name} Étant donné Miikka drinks water instead
Localized prefixes
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.kws[0].full_name} Oletetaan we don't drink too many beers
- Should Be Equal ${tc.kws[1].full_name} Kun we are in
- Should Be Equal ${tc.kws[2].full_name} mutta we don't drink too many beers
- Should Be Equal ${tc.kws[3].full_name} Ja time
- Should Be Equal ${tc.kws[4].full_name} Niin we get this feature ready today
- Should Be Equal ${tc.kws[5].full_name} ja we don't drink too many beers
+ Should Be Equal ${tc[0].full_name} Oletetaan we don't drink too many beers
+ Should Be Equal ${tc[1].full_name} Kun we are in
+ Should Be Equal ${tc[2].full_name} mutta we don't drink too many beers
+ Should Be Equal ${tc[3].full_name} Ja time
+ Should Be Equal ${tc[4].full_name} Niin we get this feature ready today
+ Should Be Equal ${tc[5].full_name} ja we don't drink too many beers
Prefix consisting of multiple words
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.kws[0].full_name} Étant donné multipart prefixes didn't work with RF 6.0
- Should Be Equal ${tc.kws[1].full_name} Zakładając, że multipart prefixes didn't work with RF 6.0
- Should Be Equal ${tc.kws[2].full_name} Diyelim ki multipart prefixes didn't work with RF 6.0
- Should Be Equal ${tc.kws[3].full_name} Eğer ki multipart prefixes didn't work with RF 6.0
- Should Be Equal ${tc.kws[4].full_name} O zaman multipart prefixes didn't work with RF 6.0
- Should Be Equal ${tc.kws[5].full_name} В случай че multipart prefixes didn't work with RF 6.0
- Should Be Equal ${tc.kws[6].full_name} Fie ca multipart prefixes didn't work with RF 6.0
+ Should Be Equal ${tc[0].full_name} Étant donné que multipart prefixes didn't work with RF 6.0
+ Should Be Equal ${tc[1].full_name} Zakładając, że multipart prefixes didn't work with RF 6.0
+ Should Be Equal ${tc[2].full_name} Diyelim ki multipart prefixes didn't work with RF 6.0
+ Should Be Equal ${tc[3].full_name} Eğer ki multipart prefixes didn't work with RF 6.0
+ Should Be Equal ${tc[4].full_name} O zaman multipart prefixes didn't work with RF 6.0
+ Should Be Equal ${tc[5].full_name} В случай че multipart prefixes didn't work with RF 6.0
+ Should Be Equal ${tc[6].full_name} Fie ca multipart prefixes didn't work with RF 6.0
+
+Prefix being part of another prefix
+ ${tc} = Check Test Case ${TEST NAME}
+ Should Be Equal ${tc[0].full_name} Étant donné que l'utilisateur se trouve sur la page de connexion
+ Should Be Equal ${tc[1].full_name} étant Donné QUE l'utilisateur SE trouve sur la pAGe de connexioN
+ Should Be Equal ${tc[2].full_name} Étant donné que if multiple prefixes match, longest prefix wins
Prefix must be followed by space
Check Test Case ${TEST NAME}
diff --git a/atest/robot/keywords/positional_only_args.robot b/atest/robot/keywords/positional_only_args.robot
index b6969a820ed..2644754cd9b 100644
--- a/atest/robot/keywords/positional_only_args.robot
+++ b/atest/robot/keywords/positional_only_args.robot
@@ -1,16 +1,18 @@
*** Settings ***
Suite Setup Run Tests ${EMPTY} keywords/positional_only_args.robot
-Force Tags require-py3.8
Resource atest_resource.robot
*** Test Cases ***
Normal usage
Check Test Case ${TESTNAME}
-Named syntax is not used
+Default values
Check Test Case ${TESTNAME}
-Default values
+Positional only value can contain '=' without it being considered named argument
+ Check Test Case ${TESTNAME}
+
+Name of positional only argument can be used with kwargs
Check Test Case ${TESTNAME}
Type conversion
@@ -19,16 +21,9 @@ Type conversion
Too few arguments
Check Test Case ${TESTNAME} 1
Check Test Case ${TESTNAME} 2
+ Check Test Case ${TESTNAME} 3
Too many arguments
Check Test Case ${TESTNAME} 1
Check Test Case ${TESTNAME} 2
-
-Named argument syntax doesn't work after valid named arguments
- Check Test Case ${TESTNAME}
-
-Name can be used with kwargs
- Check Test Case ${TESTNAME}
-
-Mandatory positional-only missing with kwargs
- Check Test Case ${TESTNAME}
+ Check Test Case ${TESTNAME} 3
diff --git a/atest/robot/keywords/private.robot b/atest/robot/keywords/private.robot
index f85ee0a7bec..f8a13aef81c 100644
--- a/atest/robot/keywords/private.robot
+++ b/atest/robot/keywords/private.robot
@@ -5,40 +5,40 @@ Resource atest_resource.robot
*** Test Cases ***
Valid Usage With Local Keyword
${tc}= Check Test Case ${TESTNAME}
- Length Should Be ${tc.body[0].body} 1
+ Length Should Be ${tc[0].body} 1
Invalid Usage With Local Keyword
${tc}= Check Test Case ${TESTNAME}
- Private Call Warning Should Be Private Keyword ${tc.body[0].body[0]} ${ERRORS[0]}
- Length Should Be ${tc.body[0].body} 2
+ Private Call Warning Should Be Private Keyword ${tc[0, 0]} ${ERRORS[0]}
+ Length Should Be ${tc[0].body} 2
Valid Usage With Resource Keyword
${tc}= Check Test Case ${TESTNAME}
- Length Should Be ${tc.body[0].body} 1
+ Length Should Be ${tc[0].body} 1
Invalid Usage With Resource Keyword
${tc}= Check Test Case ${TESTNAME}
- Private Call Warning Should Be private.Private Keyword In Resource ${tc.body[0].body[0]} ${ERRORS[1]}
- Length Should Be ${tc.body[0].body} 2
+ Private Call Warning Should Be private.Private Keyword In Resource ${tc[0, 0]} ${ERRORS[1]}
+ Length Should Be ${tc[0].body} 2
Invalid Usage in Resource File
${tc}= Check Test Case ${TESTNAME}
- Private Call Warning Should Be private2.Private Keyword In Resource 2 ${tc.body[0].body[0].body[0]} ${ERRORS[2]}
- Length Should Be ${tc.body[0].body[0].body} 2
+ Private Call Warning Should Be private2.Private Keyword In Resource 2 ${tc[0, 0, 0]} ${ERRORS[2]}
+ Length Should Be ${tc[0, 0].body} 2
Local Private Keyword In Resource File Has Precedence Over Keywords In Another Resource
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.body[0].body[0].body[0].msgs[0]} private.resource
- Check Log Message ${tc.body[0].body[1].body[0].msgs[0]} private.resource
+ Check Log Message ${tc[0, 0, 0, 0]} private.resource
+ Check Log Message ${tc[0, 1, 0, 0]} private.resource
Search Order Has Precedence Over Local Private Keyword In Resource File
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.body[0].body[0].body[0].msgs[0]} private2.resource
+ Check Log Message ${tc[0, 0, 0, 0]} private2.resource
Imported Public Keyword Has Precedence Over Imported Private Keywords
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.body[0].body[0].msgs[0]} private2.resource
- Check Log Message ${tc.body[1].body[0].body[0].msgs[0]} private2.resource
+ Check Log Message ${tc[0, 0, 0]} private2.resource
+ Check Log Message ${tc[1, 0, 0, 0]} private2.resource
If All Keywords Are Private Raise Multiple Keywords Found
Check Test Case ${TESTNAME}
diff --git a/atest/robot/keywords/trace_log_keyword_arguments.robot b/atest/robot/keywords/trace_log_keyword_arguments.robot
index 2b651548cbe..23523614e19 100644
--- a/atest/robot/keywords/trace_log_keyword_arguments.robot
+++ b/atest/robot/keywords/trace_log_keyword_arguments.robot
@@ -37,6 +37,11 @@ Variable Number of Arguments
... \${mand}='mandatory' | \@{vargs}=[]
... 'mandatory'
+Named only
+ Check Argument Value Trace
+ ... \${no1}='a' | \${no2}='b'
+ ... no1='a' | no2='b'
+
Kwargs
Check Argument Value Trace
... \&{kwargs}={}
@@ -46,8 +51,8 @@ Kwargs
All args
Check Argument Value Trace
- ... \${positional}='1' | \@{varargs}=['2', '3'] | \&{kwargs}={'d': '4'}
- ... '1' | '2' | '3' | d='4'
+ ... \${positional}='1' | \@{varargs}=['2', '3'] | \${named_only}='4' | \&{kwargs}={'free': '5'}
+ ... '1' | '2' | '3' | named_only='4' | free='5'
Non String Object as Argument
Check Argument Value Trace
@@ -68,18 +73,18 @@ Object With Unicode Repr as Argument
Arguments With Run Keyword
${tc}= Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[1].msgs[0]} Arguments: [ '\${keyword name}' | '\@{VALUES}' ] TRACE
- Check Log Message ${tc.kws[1].kws[0].msgs[0]} Arguments: [ 'a' | 'b' | 'c' | 'd' ] TRACE
+ Check Log Message ${tc[1, 0]} Arguments: [ '\${keyword name}' | '\@{VALUES}' ] TRACE
+ Check Log Message ${tc[1, 1, 0]} Arguments: [ 'a' | 'b' | 'c' | 'd' ] TRACE
Embedded Arguments
${tc}= Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} Arguments: [ \${first}='foo' | \${second}=42 | \${what}='UK' ] TRACE
- Check Log Message ${tc.kws[1].msgs[0]} Arguments: [ 'bar' | 'Embedded Arguments' ] TRACE
- Check Log Message ${tc.kws[2].msgs[0]} Arguments: [ \${embedded}='embedded' | \${normal}='argument' ] TRACE
- Check Log Message ${tc.kws[3].msgs[0]} Arguments: [ \${embedded}='embedded' | \${normal}='argument' ] TRACE
- FOR ${kw} IN @{tc.kws}
- Check Log Message ${kw.msgs[-1]} Return: None TRACE
- Length Should Be ${kw.msgs} 2
+ Check Log Message ${tc[0, 0]} Arguments: [ \${first}='foo' | \${second}=42 | \${what}='UK' ] TRACE
+ Check Log Message ${tc[1, 0]} Arguments: [ 'bar' | 'Embedded Arguments' ] TRACE
+ Check Log Message ${tc[2, 0]} Arguments: [ \${embedded}='embedded' | \${normal}='argument' ] TRACE
+ Check Log Message ${tc[3, 0]} Arguments: [ \${embedded}='embedded' | \${normal}='argument' ] TRACE
+ FOR ${kw} IN @{tc.body}
+ Check Log Message ${kw[-1]} Return: None TRACE
+ Length Should Be ${kw.messages} 2
END
*** Keywords ***
@@ -88,7 +93,7 @@ Check Argument Value Trace
${tc} = Check Test Case ${TEST NAME}
${length} = Get Length ${expected}
FOR ${index} IN RANGE 0 ${length}
- Check Log Message ${tc.kws[${index}].msgs[0]} Arguments: [ ${expected}[${index}] ] TRACE
+ Check Log Message ${tc[${index}, 0]} Arguments: [ ${expected}[${index}] ] TRACE
END
Check UKW Default, LKW Default, UKW Varargs, and LKW Varargs
diff --git a/atest/robot/keywords/trace_log_return_value.robot b/atest/robot/keywords/trace_log_return_value.robot
index 3bced7ccaa4..acf1d128701 100644
--- a/atest/robot/keywords/trace_log_return_value.robot
+++ b/atest/robot/keywords/trace_log_return_value.robot
@@ -5,35 +5,35 @@ Resource atest_resource.robot
*** Test Cases ***
Return from user keyword
${test} = Check Test Case ${TESTNAME}
- Check Log Message ${test.kws[0].msgs[1]} Return: 'value' TRACE
- Check Log Message ${test.kws[0].kws[0].msgs[1]} Return: 'value' TRACE
+ Check Log Message ${test[0, 3]} Return: 'value' TRACE
+ Check Log Message ${test[0, 1, 1]} Return: 'value' TRACE
Return from library keyword
${test} = Check Test Case ${TESTNAME}
- Check Log Message ${test.kws[0].msgs[1]} Return: 'value' TRACE
+ Check Log Message ${test[0, 1]} Return: 'value' TRACE
Return from Run Keyword
${test} = Check Test Case ${TESTNAME}
- Check Log Message ${test.kws[0].msgs[1]} Return: 'value' TRACE
- Check Log Message ${test.kws[0].kws[0].msgs[1]} Return: 'value' TRACE
+ Check Log Message ${test[0, 2]} Return: 'value' TRACE
+ Check Log Message ${test[0, 1, 1]} Return: 'value' TRACE
Return non-string value
${test} = Check Test Case ${TESTNAME}
- Check Log Message ${test.kws[0].msgs[2]} Return: 1 TRACE
+ Check Log Message ${test[0, 2]} Return: 1 TRACE
Return None
${test} = Check Test Case ${TESTNAME}
- Check Log Message ${test.kws[0].msgs[1]} Return: None TRACE
+ Check Log Message ${test[0, 1]} Return: None TRACE
Return non-ASCII string
${test} = Check Test Case ${TESTNAME}
- Check Log Message ${test.kws[0].msgs[1]} Return: "Hyvää 'Päivää'\\n" TRACE
+ Check Log Message ${test[0, 1]} Return: "Hyvää 'Päivää'\\n" TRACE
Return object with non-ASCII repr
${test} = Check Test Case ${TESTNAME}
- Check Log Message ${test.kws[0].msgs[1]} Return: Hyvä TRACE
+ Check Log Message ${test[0, 1]} Return: Hyvä TRACE
Return object with invalid repr
${test} = Check Test Case ${TESTNAME}
- Check Log Message ${test.kws[0].msgs[1]}
+ Check Log Message ${test[0, 1]}
... Return: TRACE
diff --git a/atest/robot/keywords/type_conversion/annotations.robot b/atest/robot/keywords/type_conversion/annotations.robot
index 7435614728b..df18ea4ce7f 100644
--- a/atest/robot/keywords/type_conversion/annotations.robot
+++ b/atest/robot/keywords/type_conversion/annotations.robot
@@ -75,12 +75,18 @@ Bytestring replacement
Datetime
Check Test Case ${TESTNAME}
+Datetime with now and today
+ Check Test Case ${TESTNAME}
+
Invalid datetime
Check Test Case ${TESTNAME}
Date
Check Test Case ${TESTNAME}
+Date with now and today
+ Check Test Case ${TESTNAME}
+
Invalid date
Check Test Case ${TESTNAME}
@@ -177,6 +183,9 @@ Invalid frozenset
Unknown types are not converted
Check Test Case ${TESTNAME}
+Unknown types are not converted in union
+ Check Test Case ${TESTNAME}
+
Non-type values don't cause errors
Check Test Case ${TESTNAME}
@@ -216,6 +225,9 @@ None as default with unknown type
Forward references
Check Test Case ${TESTNAME}
+Unknown forward references
+ Check Test Case ${TESTNAME}
+
@keyword decorator overrides annotations
Check Test Case ${TESTNAME}
@@ -239,3 +251,8 @@ Default value is used if explicit type conversion fails
Explicit conversion failure is used if both conversions fail
Check Test Case ${TESTNAME}
+
+Deferred evaluation of annotations
+ [Documentation] https://peps.python.org/pep-0649
+ [Tags] require-py3.14
+ Check Test Case ${TESTNAME}
diff --git a/atest/robot/keywords/type_conversion/annotations_with_typing.robot b/atest/robot/keywords/type_conversion/annotations_with_typing.robot
index bf906b1fc66..90da0f1516e 100644
--- a/atest/robot/keywords/type_conversion/annotations_with_typing.robot
+++ b/atest/robot/keywords/type_conversion/annotations_with_typing.robot
@@ -75,7 +75,10 @@ TypedDict
Stringified TypedDict types
Check Test Case ${TESTNAME}
-Optional TypedDict keys can be omitted
+Optional TypedDict keys can be omitted (total=False)
+ Check Test Case ${TESTNAME}
+
+Not required TypedDict keys can be omitted (NotRequired/Required)
Check Test Case ${TESTNAME}
Required TypedDict keys cannot be omitted
diff --git a/atest/robot/keywords/type_conversion/unions.robot b/atest/robot/keywords/type_conversion/unions.robot
index e0011d8a2ce..d8d9630fe8f 100644
--- a/atest/robot/keywords/type_conversion/unions.robot
+++ b/atest/robot/keywords/type_conversion/unions.robot
@@ -24,6 +24,9 @@ Union with subscripted generics and str
Union with TypedDict
Check Test Case ${TESTNAME}
+Union with str and TypedDict
+ Check Test Case ${TESTNAME}
+
Union with item not liking isinstance
Check Test Case ${TESTNAME}
diff --git a/atest/robot/keywords/user_keyword_arguments.robot b/atest/robot/keywords/user_keyword_arguments.robot
index f802c686713..bed9232e19f 100644
--- a/atest/robot/keywords/user_keyword_arguments.robot
+++ b/atest/robot/keywords/user_keyword_arguments.robot
@@ -85,22 +85,27 @@ Caller does not see modifications to varargs
Invalid Arguments Spec
[Template] Verify Invalid Argument Spec
- 0 338 Invalid argument syntax Invalid argument syntax 'no deco'.
- 1 342 Non-default after defaults Non-default argument after default arguments.
- 2 346 Default with varargs Only normal arguments accept default values, list arguments like '\@{varargs}' do not.
- 3 350 Default with kwargs Only normal arguments accept default values, dictionary arguments like '\&{kwargs}' do not.
- 4 354 Kwargs not last Only last argument can be kwargs.
- 5 358 Multiple errors Multiple errors:
- ... - Invalid argument syntax 'invalid'.
- ... - Non-default argument after default arguments.
- ... - Cannot have multiple varargs.
- ... - Only last argument can be kwargs.
+ 0 Invalid argument syntax Invalid argument syntax 'no deco'.
+ 1 Non-default after default Non-default argument after default arguments.
+ 2 Non-default after default w/ types Non-default argument after default arguments.
+ 3 Default with varargs Only normal arguments accept default values, list arguments like '\@{varargs}' do not.
+ 4 Default with kwargs Only normal arguments accept default values, dictionary arguments like '\&{kwargs}' do not.
+ 5 Multiple varargs Cannot have multiple varargs.
+ 6 Multiple varargs w/ types Cannot have multiple varargs.
+ 7 Kwargs not last Only last argument can be kwargs.
+ 8 Kwargs not last w/ types Only last argument can be kwargs.
+ 9 Multiple errors Multiple errors:
+ ... - Invalid argument syntax 'invalid'.
+ ... - Non-default argument after default arguments.
+ ... - Cannot have multiple varargs.
+ ... - Only last argument can be kwargs.
*** Keywords ***
Verify Invalid Argument Spec
- [Arguments] ${index} ${lineno} ${name} @{error}
+ [Arguments] ${index} ${name} @{error}
Check Test Case ${TEST NAME} - ${name}
- ${error} = Catenate SEPARATOR=\n @{error}
+ VAR ${error} @{error} separator=\n
+ VAR ${lineno} ${{358 + ${index} * 4}}
Error In File ${index} keywords/user_keyword_arguments.robot ${lineno}
... Creating keyword '${name}' failed:
... Invalid argument specification: ${error}
diff --git a/atest/robot/libdoc/LibDocLib.py b/atest/robot/libdoc/LibDocLib.py
index 9482485f52f..6a4663f61cd 100644
--- a/atest/robot/libdoc/LibDocLib.py
+++ b/atest/robot/libdoc/LibDocLib.py
@@ -3,15 +3,17 @@
import pprint
import shlex
from pathlib import Path
-from subprocess import run, PIPE, STDOUT
+from subprocess import PIPE, run, STDOUT
-from jsonschema import Draft202012Validator
+try:
+ from jsonschema import Draft202012Validator as JSONValidator
+except ImportError:
+ JSONValidator = None
from xmlschema import XMLSchema
from robot.api import logger
-from robot.utils import NOT_SET, SYSTEM_ENCODING
from robot.running.arguments import ArgInfo, TypeInfo
-
+from robot.utils import NOT_SET, SYSTEM_ENCODING
ROOT = Path(__file__).absolute().parent.parent.parent.parent
@@ -20,9 +22,14 @@ class LibDocLib:
def __init__(self, interpreter=None):
self.interpreter = interpreter
- self.xml_schema = XMLSchema(str(ROOT/'doc/schema/libdoc.xsd'))
- with open(ROOT/'doc/schema/libdoc.json') as f:
- self.json_schema = Draft202012Validator(json.load(f))
+ self.xml_schema = XMLSchema(str(ROOT / "doc/schema/libdoc.xsd"))
+ self.json_schema = self._load_json_schema()
+
+ def _load_json_schema(self):
+ if not JSONValidator:
+ return None
+ with open(ROOT / "doc/schema/libdoc.json", encoding="UTF-8") as f:
+ return JSONValidator(json.load(f))
@property
def libdoc(self):
@@ -30,21 +37,28 @@ def libdoc(self):
def run_libdoc(self, args):
cmd = self.libdoc + self._split_args(args)
- cmd[-1] = cmd[-1].replace('/', os.sep)
- logger.info(' '.join(cmd))
- result = run(cmd, cwd=ROOT/'src', stdout=PIPE, stderr=STDOUT,
- encoding=SYSTEM_ENCODING, timeout=120, universal_newlines=True)
+ cmd[-1] = cmd[-1].replace("/", os.sep)
+ logger.info(" ".join(cmd))
+ result = run(
+ cmd,
+ cwd=ROOT / "src",
+ stdout=PIPE,
+ stderr=STDOUT,
+ encoding=SYSTEM_ENCODING,
+ timeout=120,
+ text=True,
+ )
logger.info(result.stdout)
return result.stdout
def _split_args(self, args):
lexer = shlex.shlex(args, posix=True)
- lexer.escape = ''
+ lexer.escape = ""
lexer.whitespace_split = True
return list(lexer)
def get_libdoc_model_from_html(self, path):
- with open(path, encoding='UTF-8') as html_file:
+ with open(path, encoding="UTF-8") as html_file:
model_string = self._find_model(html_file)
model = json.loads(model_string)
logger.info(pprint.pformat(model))
@@ -52,33 +66,46 @@ def get_libdoc_model_from_html(self, path):
def _find_model(self, html_file):
for line in html_file:
- if line.startswith('libdoc = '):
- return line.split('=', 1)[1].strip(' \n;')
- raise RuntimeError('No model found from HTML')
+ if line.startswith("libdoc = "):
+ return line.split("=", 1)[1].strip(" \n;")
+ raise RuntimeError("No model found from HTML")
def validate_xml_spec(self, path):
self.xml_schema.validate(path)
def validate_json_spec(self, path):
- with open(path) as f:
+ if not self.json_schema:
+ raise RuntimeError("jsonschema module is not installed!")
+ with open(path, encoding="UTF-8") as f:
self.json_schema.validate(json.load(f))
def get_repr_from_arg_model(self, model):
- return str(ArgInfo(kind=model['kind'],
- name=model['name'],
- type=self._get_type_info(model['type']),
- default=model['default'] or NOT_SET))
+ return str(
+ ArgInfo(
+ kind=model["kind"],
+ name=model["name"],
+ type=self._get_type_info(model["type"]),
+ default=self._get_default(model["default"]),
+ )
+ )
def get_repr_from_json_arg_model(self, model):
- return str(ArgInfo(kind=model['kind'],
- name=model['name'],
- type=self._get_type_info(model['type']),
- default=model['defaultValue'] or NOT_SET))
+ return str(
+ ArgInfo(
+ kind=model["kind"],
+ name=model["name"],
+ type=self._get_type_info(model["type"]),
+ default=self._get_default(model["defaultValue"]),
+ )
+ )
def _get_type_info(self, data):
if not data:
return None
if isinstance(data, str):
return TypeInfo.from_string(data)
- nested = [self._get_type_info(n) for n in data.get('nested', ())]
- return TypeInfo(data['name'], None, nested=nested or None)
+ nested = [self._get_type_info(n) for n in data.get("nested", ())]
+ return TypeInfo(data["name"], None, nested=nested or None)
+
+ def _get_default(self, data):
+ return data if data is not None else NOT_SET
diff --git a/atest/robot/libdoc/backwards_compatibility.robot b/atest/robot/libdoc/backwards_compatibility.robot
index 587664238ce..029d9dbef29 100644
--- a/atest/robot/libdoc/backwards_compatibility.robot
+++ b/atest/robot/libdoc/backwards_compatibility.robot
@@ -64,14 +64,14 @@ Validate keyword 'Simple'
Keyword Name Should Be 1 Simple
Keyword Doc Should Be 1 Some doc.
Keyword Tags Should Be 1 example
- Keyword Lineno Should Be 1 34
+ Keyword Lineno Should Be 1 31
Keyword Arguments Should Be 1
Validate keyword 'Arguments'
Keyword Name Should Be 0 Arguments
Keyword Doc Should Be 0 ${EMPTY}
Keyword Tags Should Be 0
- Keyword Lineno Should Be 0 42
+ Keyword Lineno Should Be 0 39
Keyword Arguments Should Be 0 a b=2 *c d=4 e **f
Validate keyword 'Types'
diff --git a/atest/robot/libdoc/cli.robot b/atest/robot/libdoc/cli.robot
index 47d18c5aec2..905e60c695d 100644
--- a/atest/robot/libdoc/cli.robot
+++ b/atest/robot/libdoc/cli.robot
@@ -61,6 +61,11 @@ Theme
--theme light String ${OUTHTML} HTML String theme=light
--theme NoNe String ${OUTHTML} HTML String theme=
+Language
+ --language EN String ${OUTHTML} HTML String lang=en
+ --language fI String ${OUTHTML} HTML String lang=fi
+ --language NoNe String ${OUTHTML} HTML String language=
+
Relative path with Python libraries
[Template] NONE
${dir in libdoc exec dir}= Normalize Path ${ROBOTPATH}/../TempDirInExecDir
@@ -86,12 +91,14 @@ Non-existing resource
*** Keywords ***
Run Libdoc And Verify Created Output File
- [Arguments] ${args} ${format} ${name} ${version}= ${path}=${OUTHTML} ${theme}= ${quiet}=False
+ [Arguments] ${args} ${format} ${name} ${version}= ${path}=${OUTHTML} ${theme}= ${lang}= ${quiet}=False
${stdout} = Run Libdoc ${args}
Run Keyword ${format} Doc Should Have Been Created ${path} ${name} ${version}
File Should Have Correct Line Separators ${path}
IF "${theme}"
File Should Contain ${path} "theme": "${theme}"
+ ELSE IF "${lang}"
+ File Should Contain ${path} "lang": "${lang}"
ELSE
File Should Not Contain ${path} "theme":
END
@@ -105,7 +112,7 @@ Run Libdoc And Verify Created Output File
HTML Doc Should Have Been Created
[Arguments] ${path} ${name} ${version}
${libdoc}= Get File ${path}
- Should Start With ${libdoc} ${HTML DOC} HTML --format jSoN --specdocformat hTML DocFormat.py
${HTML DOC}
HTML --format jSoN DocFormat.py
${HTML DOC}
HTML --docfor RoBoT -f JSON -s HTML DocFormatHtml.py
@@ -68,6 +70,7 @@ Format from XML spec
Format from JSON RAW spec
[Template] NONE
+ [Tags] require-jsonschema
Test Format In JSON ${RAW DOC} ROBOT -F Robot -s RAW lib=DocFormat.py
Copy File ${OUTJSON} ${OUTBASE}-2.json
Test Format In JSON
${HTML DOC}
HTML lib=${OUTBASE}-2.json
@@ -80,6 +83,7 @@ Format from LIBSPEC spec
Format from JSON spec
[Template] NONE
+ [Tags] require-jsonschema
Test Format In JSON
${HTML DOC}
HTML -F Robot lib=DocFormat.py
Copy File ${OUTJSON} ${OUTBASE}-2.json
Test Format In JSON
${HTML DOC}
HTML lib=${OUTBASE}-2.json
diff --git a/atest/robot/libdoc/dynamic_library.robot b/atest/robot/libdoc/dynamic_library.robot
index a3adf492b29..ea4698aca0b 100644
--- a/atest/robot/libdoc/dynamic_library.robot
+++ b/atest/robot/libdoc/dynamic_library.robot
@@ -39,7 +39,7 @@ Init arguments
Init Source Info
Keyword Should Not Have Source 0 xpath=inits/init
- Keyword Lineno Should Be 0 9 xpath=inits/init
+ Keyword Lineno Should Be 0 10 xpath=inits/init
Keyword names
Keyword Name Should Be 0 0
@@ -101,7 +101,7 @@ No keyword source info
Keyword source info
Keyword Name Should Be 14 Source Info
Keyword Should Not Have Source 14
- Keyword Lineno Should Be 14 83
+ Keyword Lineno Should Be 14 90
Keyword source info with different path than library
Keyword Name Should Be 16 Source Path Only
diff --git a/atest/robot/libdoc/html_output.robot b/atest/robot/libdoc/html_output.robot
index 94967285a0e..d259c49bc7d 100644
--- a/atest/robot/libdoc/html_output.robot
+++ b/atest/robot/libdoc/html_output.robot
@@ -33,7 +33,7 @@ Keyword Arguments
[Template] Verify Argument Models
${MODEL}[keywords][0][args]
${MODEL}[keywords][1][args] a1=d *a2
- ${MODEL}[keywords][6][args] arg=hyv\\xe4
+ ${MODEL}[keywords][6][args] arg=hyvä
${MODEL}[keywords][9][args] arg=hyvä
${MODEL}[keywords][10][args] a=1 b=True c=(1, 2, None)
${MODEL}[keywords][11][args] arg=\\ robot \\ escapers\\n\\t\\r \\ \\
diff --git a/atest/robot/libdoc/json_output.robot b/atest/robot/libdoc/json_output.robot
index cfcaf4045a2..deec2eb1cf4 100644
--- a/atest/robot/libdoc/json_output.robot
+++ b/atest/robot/libdoc/json_output.robot
@@ -2,6 +2,7 @@
Resource libdoc_resource.robot
Suite Setup Run Libdoc And Parse Model From JSON ${TESTDATADIR}/module.py
Test Template Should Be Equal Multiline
+Test Tags require-jsonschema
*** Test Cases ***
Name
@@ -33,7 +34,7 @@ Keyword Arguments
[Template] Verify Argument Models
${MODEL}[keywords][0][args]
${MODEL}[keywords][1][args] a1=d *a2
- ${MODEL}[keywords][6][args] arg=hyv\\xe4
+ ${MODEL}[keywords][6][args] arg=hyvä
${MODEL}[keywords][9][args] arg=hyvä
${MODEL}[keywords][10][args] a=1 b=True c=(1, 2, None)
${MODEL}[keywords][11][args] arg=\\ robot \\ escapers\\n\\t\\r \\ \\
diff --git a/atest/robot/libdoc/module_library.robot b/atest/robot/libdoc/module_library.robot
index f05c7d6f054..deb44bffdb7 100644
--- a/atest/robot/libdoc/module_library.robot
+++ b/atest/robot/libdoc/module_library.robot
@@ -44,7 +44,7 @@ Keyword Arguments
Keyword Arguments Should Be 12 a b *args **kwargs
Non-ASCII Bytes Defaults
- Keyword Arguments Should Be 6 arg=hyv\\xe4
+ Keyword Arguments Should Be 6 arg=hyvä
Non-ASCII String Defaults
Keyword Arguments Should Be 9 arg=hyvä
@@ -100,9 +100,9 @@ Keyword tags
Keyword source info
Keyword Name Should Be 0 Get Hello
Keyword Should Not Have Source 0
- Keyword Lineno Should Be 0 17
+ Keyword Lineno Should Be 0 16
Keyword source info with decorated function
Keyword Name Should Be 13 Takes \${embedded} \${args}
Keyword Should Not Have Source 13
- Keyword Lineno Should Be 13 71
+ Keyword Lineno Should Be 13 70
diff --git a/atest/robot/libdoc/python_library.robot b/atest/robot/libdoc/python_library.robot
index e0d09b4c784..73f295ed31a 100644
--- a/atest/robot/libdoc/python_library.robot
+++ b/atest/robot/libdoc/python_library.robot
@@ -26,7 +26,7 @@ Scope
Source info
Source should be ${CURDIR}/../../../src/robot/libraries/Telnet.py
- Lineno should be 36
+ Lineno should be 37
Spec version
Spec version should be correct
@@ -45,7 +45,7 @@ Init Arguments
Init Source Info
Keyword Should Not Have Source 0 xpath=inits/init
- Keyword Lineno Should Be 0 281 xpath=inits/init
+ Keyword Lineno Should Be 0 283 xpath=inits/init
Keyword Names
Keyword Name Should Be 0 Close All Connections
@@ -76,39 +76,38 @@ Keyword Source Info
# This keyword is from the "main library".
Keyword Name Should Be 0 Close All Connections
Keyword Should Not Have Source 0
- Keyword Lineno Should Be 0 470
+ Keyword Lineno Should Be 0 513
# This keyword is from an external library component.
Keyword Name Should Be 7 Read Until Prompt
Keyword Should Not Have Source 7
- Keyword Lineno Should Be 7 1009
+ Keyword Lineno Should Be 7 1083
KwArgs and VarArgs
- Run Libdoc And Parse Output Process
- Keyword Name Should Be 7 Run Process
- Keyword Arguments Should Be 7 command *arguments **configuration
+ Run Libdoc And Parse Output ${TESTDATADIR}/KwArgs.py
+ Keyword Arguments Should Be 2 *varargs **kwargs
+ Keyword Arguments Should Be 3 a / b c=d *e f g=h **i
Keyword-only Arguments
- Run Libdoc And Parse Output ${TESTDATADIR}/KeywordOnlyArgs.py
+ Run Libdoc And Parse Output ${TESTDATADIR}/KwArgs.py
Keyword Arguments Should Be 0 * kwo
Keyword Arguments Should Be 1 *varargs kwo another=default
Positional-only Arguments
- [Tags] require-py3.8
Run Libdoc And Parse Output ${DATADIR}/keywords/PositionalOnly.py
- Keyword Arguments Should Be 2 arg /
+ Keyword Arguments Should Be 1 arg /
Keyword Arguments Should Be 5 posonly / normal
Keyword Arguments Should Be 0 required optional=default /
- Keyword Arguments Should Be 4 first: int second: float /
+ Keyword Arguments Should Be 3 first: int second: float /
Decorators
Run Libdoc And Parse Output ${TESTDATADIR}/Decorators.py
Keyword Name Should Be 0 Keyword Using Decorator
Keyword Arguments Should Be 0 *args **kwargs
Keyword Should Not Have Source 0
- Keyword Lineno Should Be 0 8
+ Keyword Lineno Should Be 0 7
Keyword Name Should Be 1 Keyword Using Decorator With Wraps
Keyword Arguments Should Be 1 args are preserved=True
- Keyword Lineno Should Be 1 26
+ Keyword Lineno Should Be 1 27
Documentation set in __init__
Run Libdoc And Parse Output ${TESTDATADIR}/DocSetInInit.py
@@ -135,3 +134,8 @@ Deprecation
...
... RF and Libdoc don't consider this being deprecated.
Keyword Should Not Be Deprecated 3
+
+NOT_SET as default value
+ Run Libdoc And Parse Output Collections
+ Keyword Name Should Be 17 Get From Dictionary
+ Keyword Arguments Should Be 17 dictionary key default=
diff --git a/atest/robot/libdoc/return_type_json.robot b/atest/robot/libdoc/return_type_json.robot
index 9a2851643ee..2a2de45eff5 100644
--- a/atest/robot/libdoc/return_type_json.robot
+++ b/atest/robot/libdoc/return_type_json.robot
@@ -2,6 +2,7 @@
Suite Setup Run Libdoc And Parse Model From JSON ${TESTDATADIR}/ReturnType.py
Test Template Return type should be
Resource libdoc_resource.robot
+Test Tags require-jsonschema
*** Test Cases ***
No return
diff --git a/atest/robot/output/LegacyOutputHelper.py b/atest/robot/output/LegacyOutputHelper.py
index 2713985be77..f9e558a5ccf 100644
--- a/atest/robot/output/LegacyOutputHelper.py
+++ b/atest/robot/output/LegacyOutputHelper.py
@@ -2,12 +2,12 @@
def mask_changing_parts(path):
- with open(path) as file:
+ with open(path, encoding="UTF-8") as file:
content = file.read()
for pattern, replace in [
(r'"20\d{6} \d{2}:\d{2}:\d{2}\.\d{3}"', '"[timestamp]"'),
(r'generator=".*?"', 'generator="[generator]"'),
- (r'source=".*?"', 'source="[source]"')
+ (r'source=".*?"', 'source="[source]"'),
]:
content = re.sub(pattern, replace, content)
return content
diff --git a/atest/robot/output/LogDataFinder.py b/atest/robot/output/LogDataFinder.py
index 18f11d08051..98d731cf595 100644
--- a/atest/robot/output/LogDataFinder.py
+++ b/atest/robot/output/LogDataFinder.py
@@ -26,25 +26,27 @@ def get_all_stats(path):
def _get_output_line(path, prefix):
- logger.info("Getting '%s' from '%s'."
- % (prefix, path, path), html=True)
- prefix += ' = '
- with open(path, encoding='UTF-8') as file:
+ logger.info(
+ f"Getting '{prefix}' from '{path}'.",
+ html=True,
+ )
+ prefix += " = "
+ with open(path, encoding="UTF-8") as file:
for line in file:
if line.startswith(prefix):
- logger.info('Found: %s' % line)
- return line[len(prefix):-2]
+ logger.info(f"Found: {line}")
+ return line[len(prefix) : -2]
def verify_stat(stat, *attrs):
- stat.pop('elapsed')
+ stat.pop("elapsed")
expected = dict(_get_expected_stat(attrs))
if stat != expected:
- raise WrongStat('\n%-9s: %s\n%-9s: %s' % ('Got', stat, 'Expected', expected))
+ raise WrongStat(f"\nGot : {stat}\nExpected : {expected}")
def _get_expected_stat(attrs):
- for key, value in (a.split(':', 1) for a in attrs):
+ for key, value in (a.split(":", 1) for a in attrs):
value = int(value) if value.isdigit() else str(value)
yield str(key), value
diff --git a/atest/robot/output/flatten_keyword.robot b/atest/robot/output/flatten_keyword.robot
deleted file mode 100644
index 008427d710f..00000000000
--- a/atest/robot/output/flatten_keyword.robot
+++ /dev/null
@@ -1,172 +0,0 @@
-*** Settings ***
-Suite Setup Run And Rebot Flattened
-Resource atest_resource.robot
-
-*** Variables ***
-${FLATTEN} --FlattenKeywords NAME:Keyword3
-... --flat name:key*others
-... --FLAT name:builtin.*
-... --flat TAG:flattenNOTkitty
-... --flatten "name:Flatten controls in keyword"
-... --log log.html
-${FLATTENED} Content flattened.
-${ERROR} [ ERROR ] Invalid value for option '--flattenkeywords': Expected 'FOR', 'WHILE', 'ITERATION', 'TAG:' or 'NAME:', got 'invalid'.${USAGE TIP}\n
-
-*** Test Cases ***
-Non-matching keyword is not flattened
- Should Be Equal ${TC.kws[0].message} ${EMPTY}
- Should Be Equal ${TC.kws[0].doc} Doc of keyword 2
- Length Should Be ${TC.kws[0].kws} 2
- Length Should Be ${TC.kws[0].msgs} 0
- Check Log Message ${TC.kws[0].kws[0].msgs[0]} 2
- Check Log Message ${TC.kws[0].kws[1].kws[1].msgs[0]} 1
-
-Exact match
- Should Be Equal ${TC.kws[1].message} *HTML* ${FLATTENED}
- Should Be Equal ${TC.kws[1].doc} Doc of keyword 3
- Length Should Be ${TC.kws[1].kws} 0
- Length Should Be ${TC.kws[1].msgs} 3
- Check Log Message ${TC.kws[1].msgs[0]} 3
- Check Log Message ${TC.kws[1].msgs[1]} 2
- Check Log Message ${TC.kws[1].msgs[2]} 1
-
-Pattern match
- Should Be Equal ${TC.kws[2].message} *HTML* ${FLATTENED}
- Should Be Equal ${TC.kws[2].doc} ${EMPTY}
- Length Should Be ${TC.kws[2].kws} 0
- Length Should Be ${TC.kws[2].msgs} 6
- Check Log Message ${TC.kws[2].msgs[0]} 3
- Check Log Message ${TC.kws[2].msgs[1]} 2
- Check Log Message ${TC.kws[2].msgs[2]} 1
- Check Log Message ${TC.kws[2].msgs[3]} 2
- Check Log Message ${TC.kws[2].msgs[4]} 1
- Check Log Message ${TC.kws[2].msgs[5]} 1
-
-Tag match when keyword has no message
- Should Be Equal ${TC.kws[5].message} *HTML* ${FLATTENED}
- Should Be Equal ${TC.kws[5].doc} ${EMPTY}
- Length Should Be ${TC.kws[5].kws} 0
- Length Should Be ${TC.kws[5].msgs} 1
-
-Tag match when keyword has message
- Should Be Equal ${TC.kws[6].message} *HTML* Expected e&<aped failure!${FLATTENED}
- Should Be Equal ${TC.kws[6].doc} Doc of flat keyword.
- Length Should Be ${TC.kws[6].kws} 0
- Length Should Be ${TC.kws[6].msgs} 1
-
-Match full name
- Should Be Equal ${TC.kws[3].message} *HTML* ${FLATTENED}
- Should Be Equal ${TC.kws[3].doc} Logs the given message with the given level.
- Length Should Be ${TC.kws[3].kws} 0
- Length Should Be ${TC.kws[3].msgs} 1
- Check Log Message ${TC.kws[3].msgs[0]} Flatten me too!!
-
-Flattened in log after execution
- Should Contain ${LOG} "*Content flattened."
-
-Flatten controls in keyword
- ${tc} = Check Test Case ${TEST NAME}
- Length Should Be ${tc.body[0].body.filter(messages=False)} 0
- Length Should Be ${tc.body[0].body.filter(messages=True)} 23
- Length Should Be ${tc.body[0].body} 23
- @{expected} = Create List
- ... Outside IF Inside IF 1 Nested IF
- ... 3 2 1 BANG!
- ... FOR: 0 1 FOR: 1 1 FOR: 2 1
- ... WHILE: 2 1 \${i} = 1 WHILE: 1 1 \${i} = 0
- ... AssertionError 1 finally
- FOR ${msg} ${exp} IN ZIP ${tc.body[0].body} ${expected}
- Check Log Message ${msg} ${exp} level=IGNORE
- END
-
-Flatten FOR
- Run Rebot --flatten For ${OUTFILE COPY}
- ${tc} = Check Test Case FOR loop
- Should Be Equal ${tc.kws[0].type} FOR
- Should Be Equal ${tc.kws[0].message} *HTML* ${FLATTENED}
- Length Should Be ${tc.kws[0].kws} 0
- Length Should Be ${tc.kws[0].msgs} 60
- FOR ${index} IN RANGE 10
- Check Log Message ${tc.kws[0].msgs[${index * 6 + 0}]} index: ${index}
- Check Log Message ${tc.kws[0].msgs[${index * 6 + 1}]} 3
- Check Log Message ${tc.kws[0].msgs[${index * 6 + 2}]} 2
- Check Log Message ${tc.kws[0].msgs[${index * 6 + 3}]} 1
- Check Log Message ${tc.kws[0].msgs[${index * 6 + 4}]} 2
- Check Log Message ${tc.kws[0].msgs[${index * 6 + 5}]} 1
- END
-
-Flatten FOR iterations
- Run Rebot --flatten ForItem ${OUTFILE COPY}
- ${tc} = Check Test Case FOR loop
- Should Be Equal ${tc.kws[0].type} FOR
- Should Be Equal ${tc.kws[0].message} ${EMPTY}
- Length Should Be ${tc.kws[0].kws} 10
- Should Be Empty ${tc.kws[0].msgs}
- FOR ${index} IN RANGE 10
- Should Be Equal ${tc.kws[0].kws[${index}].type} ITERATION
- Should Be Equal ${tc.kws[0].kws[${index}].message} *HTML* ${FLATTENED}
- Length Should Be ${tc.kws[0].kws[${index}].kws} 0
- Length Should Be ${tc.kws[0].kws[${index}].msgs} 6
- Check Log Message ${tc.kws[0].kws[${index}].msgs[0]} index: ${index}
- Check Log Message ${tc.kws[0].kws[${index}].msgs[1]} 3
- Check Log Message ${tc.kws[0].kws[${index}].msgs[2]} 2
- Check Log Message ${tc.kws[0].kws[${index}].msgs[3]} 1
- Check Log Message ${tc.kws[0].kws[${index}].msgs[4]} 2
- Check Log Message ${tc.kws[0].kws[${index}].msgs[5]} 1
- END
-
-Flatten WHILE
- Run Rebot --flatten WHile ${OUTFILE COPY}
- ${tc} = Check Test Case WHILE loop
- Should Be Equal ${tc.body[1].type} WHILE
- Should Be Equal ${tc.body[1].message} *HTML* ${FLATTENED}
- Length Should Be ${tc.body[1].kws} 0
- Length Should Be ${tc.body[1].msgs} 70
- FOR ${index} IN RANGE 10
- Check Log Message ${tc.body[1].msgs[${index * 7 + 0}]} index: ${index}
- Check Log Message ${tc.body[1].msgs[${index * 7 + 1}]} 3
- Check Log Message ${tc.body[1].msgs[${index * 7 + 2}]} 2
- Check Log Message ${tc.body[1].msgs[${index * 7 + 3}]} 1
- Check Log Message ${tc.body[1].msgs[${index * 7 + 4}]} 2
- Check Log Message ${tc.body[1].msgs[${index * 7 + 5}]} 1
- ${i}= Evaluate $index + 1
- Check Log Message ${tc.body[1].msgs[${index * 7 + 6}]} \${i} = ${i}
- END
-
-Flatten WHILE iterations
- Run Rebot --flatten iteration ${OUTFILE COPY}
- ${tc} = Check Test Case WHILE loop
- Should Be Equal ${tc.body[1].type} WHILE
- Should Be Equal ${tc.body[1].message} ${EMPTY}
- Length Should Be ${tc.body[1].body} 10
- Should Be Empty ${tc.body[1].msgs}
- FOR ${index} IN RANGE 10
- Should Be Equal ${tc.kws[1].kws[${index}].type} ITERATION
- Should Be Equal ${tc.kws[1].kws[${index}].message} *HTML* ${FLATTENED}
- Length Should Be ${tc.kws[1].kws[${index}].kws} 0
- Length Should Be ${tc.kws[1].kws[${index}].msgs} 7
- Check Log Message ${tc.kws[1].kws[${index}].msgs[0]} index: ${index}
- Check Log Message ${tc.kws[1].kws[${index}].msgs[1]} 3
- Check Log Message ${tc.kws[1].kws[${index}].msgs[2]} 2
- Check Log Message ${tc.kws[1].kws[${index}].msgs[3]} 1
- Check Log Message ${tc.kws[1].kws[${index}].msgs[4]} 2
- Check Log Message ${tc.kws[1].kws[${index}].msgs[5]} 1
- ${i}= Evaluate $index + 1
- Check Log Message ${tc.kws[1].kws[${index}].msgs[6]} \${i} = ${i}
- END
-
-Invalid usage
- Run Rebot Without Processing Output ${FLATTEN} --FlattenKeywords invalid ${OUTFILE COPY}
- Stderr Should Be Equal To ${ERROR}
- Run Tests Without Processing Output ${FLATTEN} --FlattenKeywords invalid output/flatten_keywords.robot
- Stderr Should Be Equal To ${ERROR}
-
-*** Keywords ***
-Run And Rebot Flattened
- Run Tests Without Processing Output ${FLATTEN} output/flatten_keywords.robot
- ${LOG} = Get File ${OUTDIR}/log.html
- Set Suite Variable $LOG
- Copy Previous Outfile
- Run Rebot ${FLATTEN} ${OUTFILE COPY}
- ${TC} = Check Test Case Flatten stuff
- Set Suite Variable $TC
diff --git a/atest/robot/output/flatten_keywords.robot b/atest/robot/output/flatten_keywords.robot
new file mode 100644
index 00000000000..fa66e863a4b
--- /dev/null
+++ b/atest/robot/output/flatten_keywords.robot
@@ -0,0 +1,279 @@
+*** Settings ***
+Suite Setup Run And Rebot Flattened
+Resource atest_resource.robot
+
+*** Variables ***
+${FLATTEN} --FlattenKeywords NAME:Keyword3
+... --flat name:key*others
+... --FLAT name:builtin.*
+... --flat TAG:flattenNOTkitty
+... --flatten "name:Flatten controls in keyword"
+${FLATTENED} Content flattened.
+${ERROR} [ ERROR ] Invalid value for option '--flattenkeywords': Expected 'FOR', 'WHILE', 'ITERATION', 'TAG:' or 'NAME:', got 'invalid'.${USAGE TIP}\n
+
+*** Test Cases ***
+Non-matching keyword is not flattened
+ Should Be Equal ${TC[0].message} ${EMPTY}
+ Should Be Equal ${TC[0].doc} Doc of keyword 2
+ Should Have Tags ${TC[0]} kw2
+ Should Be Equal ${TC[0].timeout} 2 minutes
+ Check Counts ${TC[0]} 0 2
+ Check Log Message ${TC[0, 0, 0]} 2
+ Check Log Message ${TC[0, 1, 1, 0]} 1
+
+Exact match
+ Should Be Equal ${TC[1].message} *HTML* ${FLATTENED}
+ Should Be Equal ${TC[1].doc} Doc of keyword 3
+ Should Have Tags ${TC[1]} kw3
+ Should Be Equal ${TC[1].timeout} 3 minutes
+ Check Counts ${TC[1]} 3
+ Check Log Message ${TC[1, 0]} 3
+ Check Log Message ${TC[1, 1]} 2
+ Check Log Message ${TC[1, 2]} 1
+
+Pattern match
+ Should Be Equal ${TC[2].message} *HTML* ${FLATTENED}
+ Should Be Equal ${TC[2].doc} ${EMPTY}
+ Should Have Tags ${TC[2]}
+ Should Be Equal ${TC[2].timeout} ${NONE}
+ Check Counts ${TC[2]} 6
+ Check Log Message ${TC[2, 0]} 3
+ Check Log Message ${TC[2, 1]} 2
+ Check Log Message ${TC[2, 2]} 1
+ Check Log Message ${TC[2, 3]} 2
+ Check Log Message ${TC[2, 4]} 1
+ Check Log Message ${TC[2, 5]} 1
+
+Tag match when keyword has no message
+ Should Be Equal ${TC[5].message} *HTML* ${FLATTENED}
+ Should Be Equal ${TC[5].doc} ${EMPTY}
+ Should Have Tags ${TC[5]} flatten hi
+ Check Counts ${TC[5]} 1
+
+Tag match when keyword has message
+ Should Be Equal ${TC[6].message} *HTML* Expected e&<aped failure!${FLATTENED}
+ Should Be Equal ${TC[6].doc} Doc of flat keyword.
+ Should Have Tags ${TC[6]} flatten hello
+ Check Counts ${TC[6]} 1
+
+Match full name
+ Should Be Equal ${TC[3].message} *HTML* ${FLATTENED}
+ Should Be Equal ${TC[3].doc} Logs the given message with the given level.
+ Check Counts ${TC[3]} 1
+ Check Log Message ${TC[3, 0]} Flatten me too!!
+
+Flattened in log after execution
+ Should Contain ${LOG} "*Content flattened."
+
+Flatten controls in keyword
+ ${tc} = Check Test Case ${TEST NAME}
+ @{expected} = Create List
+ ... Outside IF Inside IF 1 Nested IF
+ ... 3 2 1 BANG!
+ ... FOR: 0 1 FOR: 1 1 FOR: 2 1
+ ... WHILE: 2 1 \${i} = 1 WHILE: 1 1 \${i} = 0
+ ... AssertionError 1 finally
+ ... Inside GROUP \${x} = Using VAR
+ FOR ${msg} ${exp} IN ZIP ${tc[0].body} ${expected} mode=STRICT
+ Check Log Message ${msg} ${exp} level=IGNORE
+ END
+
+Flatten FOR
+ Run Rebot --flatten For ${OUTFILE COPY}
+ ${tc} = Check Test Case FOR loop
+ Should Be Equal ${tc[0].type} FOR
+ Should Be Equal ${tc[0].message} *HTML* ${FLATTENED}
+ Check Counts ${tc[0]} 60
+ FOR ${index} IN RANGE 10
+ Check Log Message ${tc[0, ${index * 6 + 0}]} index: ${index}
+ Check Log Message ${tc[0, ${index * 6 + 1}]} 3
+ Check Log Message ${tc[0, ${index * 6 + 2}]} 2
+ Check Log Message ${tc[0, ${index * 6 + 3}]} 1
+ Check Log Message ${tc[0, ${index * 6 + 4}]} 2
+ Check Log Message ${tc[0, ${index * 6 + 5}]} 1
+ END
+
+Flatten FOR iterations
+ Run Rebot --flatten ForItem ${OUTFILE COPY}
+ ${tc} = Check Test Case FOR loop
+ Should Be Equal ${tc[0].type} FOR
+ Should Be Equal ${tc[0].message} ${EMPTY}
+ Check Counts ${tc[0]} 0 10
+ FOR ${index} IN RANGE 10
+ Should Be Equal ${tc[0, ${index}].type} ITERATION
+ Should Be Equal ${tc[0, ${index}].message} *HTML* ${FLATTENED}
+ Check Counts ${tc[0, ${index}]} 6
+ Check Log Message ${tc[0, ${index}, 0]} index: ${index}
+ Check Log Message ${tc[0, ${index}, 1]} 3
+ Check Log Message ${tc[0, ${index}, 2]} 2
+ Check Log Message ${tc[0, ${index}, 3]} 1
+ Check Log Message ${tc[0, ${index}, 4]} 2
+ Check Log Message ${tc[0, ${index}, 5]} 1
+ END
+
+Flatten WHILE
+ Run Rebot --flatten WHile ${OUTFILE COPY}
+ ${tc} = Check Test Case WHILE loop
+ Should Be Equal ${tc[1].type} WHILE
+ Should Be Equal ${tc[1].message} *HTML* ${FLATTENED}
+ Check Counts ${tc[1]} 70
+ FOR ${index} IN RANGE 10
+ Check Log Message ${tc[1, ${index * 7 + 0}]} index: ${index}
+ Check Log Message ${tc[1, ${index * 7 + 1}]} 3
+ Check Log Message ${tc[1, ${index * 7 + 2}]} 2
+ Check Log Message ${tc[1, ${index * 7 + 3}]} 1
+ Check Log Message ${tc[1, ${index * 7 + 4}]} 2
+ Check Log Message ${tc[1, ${index * 7 + 5}]} 1
+ ${i}= Evaluate $index + 1
+ Check Log Message ${tc[1, ${index * 7 + 6}]} \${i} = ${i}
+ END
+
+Flatten WHILE iterations
+ Run Rebot --flatten iteration ${OUTFILE COPY}
+ ${tc} = Check Test Case WHILE loop
+ Should Be Equal ${tc[1].type} WHILE
+ Should Be Equal ${tc[1].message} ${EMPTY}
+ Check Counts ${tc[1]} 0 10
+ FOR ${index} IN RANGE 10
+ Should Be Equal ${tc[1, ${index}].type} ITERATION
+ Should Be Equal ${tc[1, ${index}].message} *HTML* ${FLATTENED}
+ Check Counts ${tc[1, ${index}]} 7
+ Check Log Message ${tc[1, ${index}, 0]} index: ${index}
+ Check Log Message ${tc[1, ${index}, 1]} 3
+ Check Log Message ${tc[1, ${index}, 2]} 2
+ Check Log Message ${tc[1, ${index}, 3]} 1
+ Check Log Message ${tc[1, ${index}, 4]} 2
+ Check Log Message ${tc[1, ${index}, 5]} 1
+ ${i}= Evaluate $index + 1
+ Check Log Message ${tc[1, ${index}, 6]} \${i} = ${i}
+ END
+
+Flatten with JSON
+ GROUP Run tests
+ VAR ${flatten}
+ ... --flattenkeywords name:Keyword3
+ ... --flatten-keywords tag:flattenNOTkitty
+ ... --flatten FOR
+ ... --flatten WHILE
+ Run Tests Without Processing Output ${flatten} --output output.json --log log.html output/flatten_keywords.robot
+ END
+ GROUP Check flattening in log after afecution.
+ ${log} = Get File ${OUTDIR}/log.html
+ Should Contain ${log} "*Content flattened."
+ END
+ GROUP Run Rebot
+ Copy File ${OUTDIR}/output.json %{TEMPDIR}/output.json
+ Run Rebot ${flatten} %{TEMPDIR}/output.json
+ END
+ GROUP Check flattening by keyword name and tags
+ ${tc} = Check Test Case Flatten stuff
+ Should Be Equal ${tc[0].message} ${EMPTY}
+ Should Be Equal ${tc[0].doc} Doc of keyword 2
+ Should Have Tags ${tc[0]} kw2
+ Should Be Equal ${tc[0].timeout} 2 minutes
+ Check Counts ${tc[0]} 0 2
+ Check Log Message ${tc[0, 0, 0]} 2
+ Check Log Message ${tc[0, 1, 1, 0]} 1
+ Should Be Equal ${tc[1].message} *HTML* ${FLATTENED}
+ Should Be Equal ${tc[1].doc} Doc of keyword 3
+ Should Have Tags ${tc[1]} kw3
+ Should Be Equal ${tc[1].timeout} 3 minutes
+ Check Counts ${tc[1]} 3
+ Check Log Message ${tc[1, 0]} 3
+ Check Log Message ${tc[1, 1]} 2
+ Check Log Message ${tc[1, 2]} 1
+ Should Be Equal ${tc[5].message} *HTML* ${FLATTENED}
+ Should Be Equal ${tc[5].doc} ${EMPTY}
+ Should Have Tags ${tc[5]} flatten hi
+ Check Counts ${tc[5]} 1
+ Should Be Equal ${tc[6].message} *HTML* Expected e&<aped failure!${FLATTENED}
+ Should Be Equal ${tc[6].doc} Doc of flat keyword.
+ Should Have Tags ${tc[6]} flatten hello
+ Check Counts ${tc[6]} 1
+ END
+ GROUP Check flattening FOR loop
+ ${tc} = Check Test Case FOR loop
+ Should Be Equal ${tc[0].type} FOR
+ Should Be Equal ${tc[0].message} *HTML* ${FLATTENED}
+ Check Counts ${tc[0]} 60
+ FOR ${index} IN RANGE 10
+ Check Log Message ${tc[0, ${index * 6 + 0}]} index: ${index}
+ Check Log Message ${tc[0, ${index * 6 + 1}]} 3
+ Check Log Message ${tc[0, ${index * 6 + 2}]} 2
+ Check Log Message ${tc[0, ${index * 6 + 3}]} 1
+ Check Log Message ${tc[0, ${index * 6 + 4}]} 2
+ Check Log Message ${tc[0, ${index * 6 + 5}]} 1
+ END
+ END
+ GROUP Check flattening WHILE loop
+ ${tc} = Check Test Case WHILE loop
+ Should Be Equal ${tc[1].type} WHILE
+ Should Be Equal ${tc[1].message} *HTML* ${FLATTENED}
+ Check Counts ${tc[1]} 70
+ FOR ${index} IN RANGE 10
+ Check Log Message ${tc[1, ${index * 7 + 0}]} index: ${index}
+ Check Log Message ${tc[1, ${index * 7 + 1}]} 3
+ Check Log Message ${tc[1, ${index * 7 + 2}]} 2
+ Check Log Message ${tc[1, ${index * 7 + 3}]} 1
+ Check Log Message ${tc[1, ${index * 7 + 4}]} 2
+ Check Log Message ${tc[1, ${index * 7 + 5}]} 1
+ ${i}= Evaluate $index + 1
+ Check Log Message ${tc[1, ${index * 7 + 6}]} \${i} = ${i}
+ END
+ END
+ GROUP Check flattening FOR and WHILE iterations
+ Run Rebot --flatten ITERATION %{TEMPDIR}/output.json
+ ${tc} = Check Test Case FOR loop
+ Should Be Equal ${tc[0].type} FOR
+ Should Be Equal ${tc[0].message} ${EMPTY}
+ Check Counts ${tc[0]} 0 10
+ FOR ${index} IN RANGE 10
+ Should Be Equal ${tc[0, ${index}].type} ITERATION
+ Should Be Equal ${tc[0, ${index}].message} *HTML* ${FLATTENED}
+ Check Counts ${tc[0, ${index}]} 6
+ Check Log Message ${tc[0, ${index}, 0]} index: ${index}
+ Check Log Message ${tc[0, ${index}, 1]} 3
+ Check Log Message ${tc[0, ${index}, 2]} 2
+ Check Log Message ${tc[0, ${index}, 3]} 1
+ Check Log Message ${tc[0, ${index}, 4]} 2
+ Check Log Message ${tc[0, ${index}, 5]} 1
+ END
+ ${tc} = Check Test Case WHILE loop
+ Should Be Equal ${tc[1].type} WHILE
+ Should Be Equal ${tc[1].message} ${EMPTY}
+ Check Counts ${tc[1]} 0 10
+ FOR ${index} IN RANGE 10
+ Should Be Equal ${tc[1, ${index}].type} ITERATION
+ Should Be Equal ${tc[1, ${index}].message} *HTML* ${FLATTENED}
+ Check Counts ${tc[1, ${index}]} 7
+ Check Log Message ${tc[1, ${index}, 0]} index: ${index}
+ Check Log Message ${tc[1, ${index}, 1]} 3
+ Check Log Message ${tc[1, ${index}, 2]} 2
+ Check Log Message ${tc[1, ${index}, 3]} 1
+ Check Log Message ${tc[1, ${index}, 4]} 2
+ Check Log Message ${tc[1, ${index}, 5]} 1
+ ${i}= Evaluate $index + 1
+ Check Log Message ${tc[1, ${index}, 6]} \${i} = ${i}
+ END
+ END
+
+Invalid usage
+ Run Rebot Without Processing Output ${FLATTEN} --FlattenKeywords invalid ${OUTFILE COPY}
+ Stderr Should Be Equal To ${ERROR}
+ Run Tests Without Processing Output ${FLATTEN} --FlattenKeywords invalid output/flatten_keywords.robot
+ Stderr Should Be Equal To ${ERROR}
+
+*** Keywords ***
+Run And Rebot Flattened
+ Run Tests Without Processing Output ${FLATTEN} --log log.html output/flatten_keywords.robot
+ ${LOG} = Get File ${OUTDIR}/log.html
+ Set Suite Variable $LOG
+ Copy Previous Outfile
+ Run Rebot ${FLATTEN} ${OUTFILE COPY}
+ ${TC} = Check Test Case Flatten stuff
+ Set Suite Variable $TC
+
+Check Counts
+ [Arguments] ${item} ${messages} ${non_messages}=0
+ Length Should Be ${item.messages} ${messages}
+ Length Should Be ${item.non_messages} ${non_messages}
diff --git a/atest/robot/output/json_output.robot b/atest/robot/output/json_output.robot
new file mode 100644
index 00000000000..d703bf2b8ec
--- /dev/null
+++ b/atest/robot/output/json_output.robot
@@ -0,0 +1,42 @@
+*** Settings ***
+Documentation JSON output is tested in detailed level using unit tests.
+Resource atest_resource.robot
+
+*** Variables ***
+${JSON} %{TEMPDIR}/output.json
+${XML} %{TEMPDIR}/output.xml
+
+*** Test Cases ***
+JSON output contains same suite information as XML output
+ Run Tests ${EMPTY} misc
+ Copy File ${OUTFILE} ${XML}
+ Run Tests Without Processing Output -o ${JSON} misc
+ Outputs Should Contain Same Data ${JSON} ${XML} ignore_timestamps=True
+
+JSON output structure
+ [Documentation] Full JSON schema validation would be good, but it's too slow with big output files.
+ ... The test after this one validates a smaller suite against a schema.
+ ${data} = Evaluate json.load(open($JSON, encoding='UTF-8'))
+ Lists Should Be Equal ${data} ${{['generator', 'generated', 'rpa', 'suite', 'statistics', 'errors']}}
+ Should Match ${data}[generator] Robot ?.* (* on *)
+ Should Match ${data}[generated] 20??-??-??T??:??:??.??????
+ Should Be Equal ${data}[rpa] ${False}
+ Should Be Equal ${data}[suite][name] Misc
+ Should Be Equal ${data}[suite][suites][1][name] Everything
+ Should Be Equal ${data}[statistics][total][skip] ${3}
+ Should Be Equal ${data}[statistics][tags][4][label] f1
+ Should Be Equal ${data}[statistics][suites][-1][id] s1-s17
+ Should Be Equal ${data}[errors][0][level] ERROR
+
+JSON output matches schema
+ [Tags] require-jsonschema
+ Run Tests Without Processing Output -o OUT.JSON misc/everything.robot
+ Validate JSON Output ${OUTDIR}/OUT.JSON
+
+Invalid JSON output file
+ ${path} = Normalize Path ${JSON}
+ Remove File ${path}
+ Create Directory ${path}
+ Run Tests Without Processing Output -o ${path} misc/pass_and_fail.robot
+ Stderr Should Match [[] ERROR ] Opening output file '${path}' failed: *${USAGE TIP}\n
+ [Teardown] Remove Directory ${JSON}
diff --git a/atest/robot/output/legacy_output.robot b/atest/robot/output/legacy_output.robot
index 2f69f066d83..017cf0cc68a 100644
--- a/atest/robot/output/legacy_output.robot
+++ b/atest/robot/output/legacy_output.robot
@@ -11,6 +11,13 @@ Legacy output with Rebot
Run Tests ${EMPTY} output/legacy.robot
Copy Previous Outfile
Run Rebot --legacy-output ${OUTFILE COPY} validate output=False
+ Validate output
+
+Legacy output with Rebot when all times are not set
+ Run Rebot --legacy-output --test Passing ${OUTFILE COPY} validate output=False
+ Should Be Equal ${SUITE.start_time} ${None}
+ Should Be Equal ${SUITE.end_time} ${None}
+ Should Contain Tests ${SUITE} Passing
*** Keywords ***
Validate output
diff --git a/atest/robot/output/listener_interface/body_items_v3.robot b/atest/robot/output/listener_interface/body_items_v3.robot
index 97c2f53cb6e..fab0a6ee538 100644
--- a/atest/robot/output/listener_interface/body_items_v3.robot
+++ b/atest/robot/output/listener_interface/body_items_v3.robot
@@ -7,7 +7,7 @@ ${SOURCE} output/listener_interface/body_items_v3/tests.robot
${MODIFIER} output/listener_interface/body_items_v3/Modifier.py
@{ALL TESTS} Library keyword User keyword Non-existing keyword
... Empty keyword Duplicate keyword Invalid keyword
-... IF TRY FOR WHILE VAR RETURN
+... IF TRY FOR WHILE WHILE with modified limit VAR RETURN
... Invalid syntax Run Keyword
*** Test Cases ***
@@ -25,40 +25,47 @@ Modify invalid keyword
Modify keyword results
${tc} = Get Test Case Invalid keyword
- Check Keyword Data ${tc.body[0]} Invalid keyword
+ Check Keyword Data ${tc[0]} Invalid keyword
... args=\${secret}
... tags=end, fixed, start
... doc=Results can be modified both in start and end!
Modify FOR
${tc} = Check Test Case FOR FAIL Listener failed me at 'b'!
- Length Should Be ${tc.body[0].body} 2
- Should Be Equal ${tc.body[0].assign}[0] secret
- Should Be Equal ${tc.body[0].body[0].assign}[\${x}] xxx
- Should Be Equal ${tc.body[0].body[1].assign}[\${x}] xxx
+ Length Should Be ${tc[0].body} 2
+ Should Be Equal ${tc[0].assign}[0] secret
+ Should Be Equal ${tc[0, 0].assign}[\${x}] xxx
+ Should Be Equal ${tc[0, 1].assign}[\${x}] xxx
Modify WHILE
${tc} = Check Test Case WHILE FAIL Fail at iteration 10.
- Length Should Be ${tc.body[0].body} 10
+ Length Should Be ${tc[0].body} 10
+
+Modify WHILE limit
+ ${tc} = Check Test Case WHILE with modified limit PASS ${EMPTY}
+ Length Should Be ${tc[1].body} 3
+ Check Log Message ${tc[1, 0, 0, 0]} \${x} = 1
+ Check Log Message ${tc[1, 1, 0, 0]} \${x} = 2
+ Check Log Message ${tc[1, 2]} Modified limit message.
Modify IF
${tc} = Check Test Case IF FAIL Executed!
- Should Be Equal ${tc.body[0].body[0].message} Secret message!
- Should Be Equal ${tc.body[0].body[1].message} Secret message!
- Should Be Equal ${tc.body[0].body[2].message} Executed!
+ Should Be Equal ${tc[0, 0].message} Secret message!
+ Should Be Equal ${tc[0, 1].message} Secret message!
+ Should Be Equal ${tc[0, 2].message} Executed!
Modify TRY
${tc} = Check Test Case TRY FAIL Not caught!
- Length Should Be ${tc.body[0].body} 3
+ Length Should Be ${tc[0].body} 3
Modify VAR
${tc} = Check Test Case VAR FAIL value != VAR by listener
- Should Be Equal ${tc.body[0].value}[0] secret
- Should Be Equal ${tc.body[1].value}[0] secret
+ Should Be Equal ${tc[0].value}[0] secret
+ Should Be Equal ${tc[1].value}[0] secret
Modify RETURN
${tc} = Check Test Case RETURN FAIL RETURN by listener != value
- Should Be Equal ${tc.body[0].body[1].values}[0] secret
+ Should Be Equal ${tc[0, 1].values}[0] secret
Validate that all methods are called correctly
Run Tests --variable VALIDATE_EVENTS:True ${SOURCE}
diff --git a/atest/robot/output/listener_interface/change_status.robot b/atest/robot/output/listener_interface/change_status.robot
new file mode 100644
index 00000000000..be97fe5db3f
--- /dev/null
+++ b/atest/robot/output/listener_interface/change_status.robot
@@ -0,0 +1,73 @@
+*** Settings ***
+Suite Setup Run Tests --listener ${DATADIR}/${MODIFIER} ${SOURCE}
+Resource atest_resource.robot
+
+*** Variables ***
+${SOURCE} output/listener_interface/body_items_v3/change_status.robot
+${MODIFIER} output/listener_interface/body_items_v3/ChangeStatus.py
+
+*** Test Cases ***
+Fail to pass
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Keyword Data ${tc[0]} BuiltIn.Fail args=Pass me! status=PASS message=Failure hidden!
+ Check Log Message ${tc[0, 0]} Pass me! level=FAIL
+ Check Keyword Data ${tc[1]} BuiltIn.Log args=I'm run. status=PASS message=
+
+Pass to fail
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Keyword Data ${tc[0]} BuiltIn.Log args=Fail me! status=FAIL message=Ooops!!
+ Check Log Message ${tc[0, 0]} Fail me! level=INFO
+ Check Keyword Data ${tc[1]} BuiltIn.Log args=I'm not run. status=NOT RUN message=
+
+Pass to fail without a message
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Keyword Data ${tc[0]} BuiltIn.Log args=Silent fail! status=FAIL message=
+ Check Keyword Data ${tc[1]} BuiltIn.Log args=I'm not run. status=NOT RUN message=
+
+Skip to fail
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Keyword Data ${tc[0]} BuiltIn.Skip args=Fail me! status=FAIL message=Failing!
+ Check Log Message ${tc[0, 0]} Fail me! level=SKIP
+ Check Keyword Data ${tc[1]} BuiltIn.Log args=I'm not run. status=NOT RUN message=
+
+Fail to skip
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Keyword Data ${tc[0]} BuiltIn.Fail args=Skip me! status=SKIP message=Skipping!
+ Check Log Message ${tc[0, 0]} Skip me! level=FAIL
+ Check Keyword Data ${tc[1]} BuiltIn.Log args=I'm not run. status=NOT RUN message=
+
+Not run to fail
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Keyword Data ${tc[0]} BuiltIn.Log args=Fail me! status=FAIL message=Ooops!!
+ Check Keyword Data ${tc[1]} BuiltIn.Log args=I'm not run. status=NOT RUN message=
+ Check Keyword Data ${tc[2]} BuiltIn.Log args=Fail me! status=FAIL message=Failing without running!
+ Check Keyword Data ${tc[3]} BuiltIn.Log args=I'm not run. status=NOT RUN message=
+
+Pass and fail to not run
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Keyword Data ${tc[0]} BuiltIn.Log args=Mark not run! status=NOT RUN message=
+ Check Keyword Data ${tc[1]} BuiltIn.Fail args=Mark not run! status=NOT RUN message=Mark not run!
+ Check Keyword Data ${tc[2]} BuiltIn.Fail args=I fail! status=FAIL message=I fail!
+
+Only message
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Keyword Data ${tc[0]} BuiltIn.Fail args=Change me! status=FAIL message=Changed!
+ Check Keyword Data ${tc[1]} Change message status=NOT RUN message=Changed!
+
+Control structures
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Control Structure ${tc[0]} FOR
+ Check Control Structure ${tc[1]} WHILE
+ Check Control Structure ${tc[2]} IF/ELSE ROOT
+ Check Control Structure ${tc[3]} TRY/EXCEPT ROOT
+
+*** Keywords ***
+Check Control Structure
+ [Arguments] ${item} ${type}
+ VAR ${msg} Handled on ${type} level.
+ Should Be Equal ${item.type} ${type}
+ Should Be Equal ${item.status} PASS
+ Should Be Equal ${item.message} ${msg}
+ Should Be Equal ${item[0].status} FAIL
+ Should Be Equal ${item[0].message} ${msg}
+ Check Keyword Data ${item[0, 0]} BuiltIn.Fail args=${msg} status=FAIL message=${msg}
diff --git a/atest/robot/output/listener_interface/keyword_arguments_v3.robot b/atest/robot/output/listener_interface/keyword_arguments_v3.robot
index 8ab077c3698..09b8b7d26b1 100644
--- a/atest/robot/output/listener_interface/keyword_arguments_v3.robot
+++ b/atest/robot/output/listener_interface/keyword_arguments_v3.robot
@@ -7,49 +7,46 @@ ${SOURCE} output/listener_interface/body_items_v3/keyword_arguments.robo
${MODIFIER} output/listener_interface/body_items_v3/ArgumentModifier.py
*** Test Cases ***
-Arguments as strings
+Library keyword arguments
${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.body[0]} Library.Library Keyword
+ Check Keyword Data ${tc[0]} Library.Library Keyword
... args=\${STATE}, number=\${123}, obj=None, escape=c:\\\\temp\\\\new
- Check Keyword Data ${tc.body[1]} Library.Library Keyword
+ Check Keyword Data ${tc[1]} Library.Library Keyword
... args=new, 123, c:\\\\temp\\\\new, NONE
+ Check Keyword Data ${tc[2]} Library.Library Keyword
+ ... args=new, number=\${42}, escape=c:\\\\temp\\\\new, obj=Object(42)
+ Check Keyword Data ${tc[3]} Library.Library Keyword
+ ... args=number=1.0, escape=c:\\\\temp\\\\new, obj=Object(1), state=new
-Arguments as tuples
+User keyword arguments
${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.body[0]} Library.Library Keyword
- ... args=\${STATE}, escape=c:\\\\temp\\\\new, obj=Object(123), number=\${123}
- Check Keyword Data ${tc.body[1]} Library.Library Keyword
- ... args=new, 1.0, obj=Object(1), escape=c:\\\\temp\\\\new
+ Check Keyword Data ${tc[0]} User keyword
+ ... args=A, B, C, D
+ Check Keyword Data ${tc[1]} User keyword
+ ... args=A, B, d=D, c=\${{"c".upper()}}
-Arguments directly as positional and named
- ${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.body[0]} Library.Library Keyword
- ... args=\${XXX}, 456, c:\\temp\\new, obj=Object(456)
- Check Keyword Data ${tc.body[1]} Library.Library Keyword
- ... args=state=\${XXX}, obj=Object(1), number=1.0, escape=c:\\temp\\new
+Invalid keyword arguments
+ ${tc} = Check Test Case Library keyword arguments
+ Check Keyword Data ${tc[4]} Non-existing
+ ... args=p, n=1 status=FAIL
Too many arguments
${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.body[0]} Library.Library Keyword
+ Check Keyword Data ${tc[0]} Library.Library Keyword
+ ... args=a, b, c, d, e, f, g status=FAIL
+ Check Keyword Data ${tc[1]} User keyword
... args=a, b, c, d, e, f, g status=FAIL
- Check Keyword Data ${tc.body[1]} Library.Library Keyword
+ Check Keyword Data ${tc[2]} Library.Library Keyword
... args=${{', '.join(str(i) for i in range(100))}} status=FAIL
Conversion error
${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.body[0]} Library.Library Keyword
+ Check Keyword Data ${tc[0]} Library.Library Keyword
... args=whatever, not a number status=FAIL
- Check Keyword Data ${tc.body[1]} Library.Library Keyword
+ Check Keyword Data ${tc[1]} Library.Library Keyword
... args=number=bad status=FAIL
-Named argument not matching
- ${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.body[0]} Library.Library Keyword
- ... args=no=match status=FAIL
- Check Keyword Data ${tc.body[1]} Library.Library Keyword
- ... args=o, k, bad=name status=FAIL
-
Positional after named
${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.body[0]} Library.Library Keyword
- ... args=positional, name=value, ooops status=FAIL
+ Check Keyword Data ${tc[0]} Library.Library Keyword
+ ... args=positional, number=-1, ooops status=FAIL
diff --git a/atest/robot/output/listener_interface/listener_failing.robot b/atest/robot/output/listener_interface/listener_failing.robot
index 9fd5417ac19..da2f6870fb2 100644
--- a/atest/robot/output/listener_interface/listener_failing.robot
+++ b/atest/robot/output/listener_interface/listener_failing.robot
@@ -43,7 +43,7 @@ Listener errors should be reported
Library listener errors should be reported
FOR ${index} ${method} IN ENUMERATE
- ... start_suite start_test start_keyword log_message
+ ... message start_suite start_test start_keyword log_message
... end_keyword end_test end_suite
Error should be reported in execution errors ${index} ${method} failing_listener
END
diff --git a/atest/robot/output/listener_interface/listener_logging.robot b/atest/robot/output/listener_interface/listener_logging.robot
index 9a4a4978cea..35d400088f4 100644
--- a/atest/robot/output/listener_interface/listener_logging.robot
+++ b/atest/robot/output/listener_interface/listener_logging.robot
@@ -10,16 +10,29 @@ Logging from listener does not break output file
All start and end methods can log warnings to execution errors
Correct warnings should be shown in execution errors
-Methods inside start_keyword and end_keyword can log normal messages
+Methods under tests can log normal messages
Correct messages should be logged to normal log
-Methods outside start_keyword and end_keyword can log messages to syslog
+Methods outside tests can log messages to syslog
+ Correct messages should be logged to syslog
+
+Logging from listener when using JSON output
+ [Setup] Run Tests With Logging Listener format=json
+ Test statuses should be correct
+ Log and report should be created
+ Correct messages should be logged to normal log
+ Correct warnings should be shown in execution errors
Correct messages should be logged to syslog
*** Keywords ***
Run Tests With Logging Listener
- ${path} = Normalize Path ${LISTENER DIR}/logging_listener.py
- Run Tests --listener ${path} -l l.html -r r.html misc/pass_and_fail.robot
+ [Arguments] ${format}=xml
+ Should Be True $format in ('xml', 'json')
+ VAR ${output} ${OUTDIR}/output.${format}
+ VAR ${options}
+ ... --listener ${LISTENER DIR}/logging_listener.py
+ ... -o ${output} -l l.html -r r.html
+ Run Tests ${options} misc/pass_and_fail.robot output=${output}
Test statuses should be correct
Check Test Case Pass
@@ -35,7 +48,7 @@ Correct warnings should be shown in execution errors
Execution errors should have messages from message and log_message methods
Check Log Message ${ERRORS[0]} message: INFO Robot Framework * WARN pattern=yes
- Check Log Message ${ERRORS[-4]} log_message: FAIL Expected failure WARN
+ Check Log Message ${ERRORS[-7]} log_message: FAIL Expected failure WARN
Correct start/end warnings should be shown in execution errors
${msgs} = Get start/end messages ${ERRORS}
@@ -49,10 +62,14 @@ Correct start/end warnings should be shown in execution errors
... @{setup}
... start_test
... @{uk}
+ ... start keyword start keyword end keyword end keyword
+ ... @{kw}
+ ... start teardown end teardown
... end_test
... start_test
... @{uk}
... @{kw}
+ ... start teardown end teardown
... end_test
... end_suite
Check Log Message ${msgs}[${index}] ${method} WARN
@@ -71,70 +88,91 @@ Get start/end messages
Correct messages should be logged to normal log
'My Keyword' has correct messages ${SUITE.setup} Suite Setup
${tc} = Check Test Case Pass
- 'My Keyword' has correct messages ${tc.kws[0]} Pass
+ Check Log Message ${tc[0]} start_test INFO
+ Check Log Message ${tc[1]} start_test WARN
+ 'My Keyword' has correct messages ${tc[2]} Pass
+ Check Log Message ${tc[5]} end_test INFO
+ Check Log Message ${tc[6]} end_test WARN
+ Teardown has correct messages ${tc.teardown}
${tc} = Check Test Case Fail
- 'My Keyword' has correct messages ${tc.kws[0]} Fail
- 'Fail' has correct messages ${tc.kws[1]}
+ Check Log Message ${tc[0]} start_test INFO
+ Check Log Message ${tc[1]} start_test WARN
+ 'My Keyword' has correct messages ${tc[2]} Fail
+ 'Fail' has correct messages ${tc[3]}
+ Check Log Message ${tc[4]} end_test INFO
+ Check Log Message ${tc[5]} end_test WARN
+ Teardown has correct messages ${tc.teardown}
'My Keyword' has correct messages
[Arguments] ${kw} ${name}
IF '${name}' == 'Suite Setup'
- ${type} = Set Variable setup
+ VAR ${type} setup
ELSE
- ${type} = Set Variable keyword
+ VAR ${type} keyword
END
- Check Log Message ${kw.body[0]} start ${type} INFO
- Check Log Message ${kw.body[1]} start ${type} WARN
- Check Log Message ${kw.body[2].body[0]} start keyword INFO
- Check Log Message ${kw.body[2].body[1]} start keyword WARN
- Check Log Message ${kw.body[2].body[2]} log_message: INFO Hello says "${name}"! INFO
- Check Log Message ${kw.body[2].body[3]} log_message: INFO Hello says "${name}"! WARN
- Check Log Message ${kw.body[2].body[4]} Hello says "${name}"! INFO
- Check Log Message ${kw.body[2].body[5]} end keyword INFO
- Check Log Message ${kw.body[2].body[6]} end keyword WARN
- Check Log Message ${kw.body[3].body[0]} start keyword INFO
- Check Log Message ${kw.body[3].body[1]} start keyword WARN
- Check Log Message ${kw.body[3].body[2]} end keyword INFO
- Check Log Message ${kw.body[3].body[3]} end keyword WARN
- Check Log Message ${kw.body[4].body[0]} start keyword INFO
- Check Log Message ${kw.body[4].body[1]} start keyword WARN
- Check Log Message ${kw.body[4].body[2]} log_message: INFO \${assign} = JUST TESTING... INFO
- Check Log Message ${kw.body[4].body[3]} log_message: INFO \${assign} = JUST TESTING... WARN
- Check Log Message ${kw.body[4].body[4]} \${assign} = JUST TESTING... INFO
- Check Log Message ${kw.body[4].body[5]} end keyword INFO
- Check Log Message ${kw.body[4].body[6]} end keyword WARN
- Check Log Message ${kw.body[5].body[0]} start var INFO
- Check Log Message ${kw.body[5].body[1]} start var WARN
- Check Log Message ${kw.body[5].body[2]} end var INFO
- Check Log Message ${kw.body[5].body[3]} end var WARN
- Check Log Message ${kw.body[6].body[0]} start keyword INFO
- Check Log Message ${kw.body[6].body[1]} start keyword WARN
- Check Log Message ${kw.body[6].body[2]} end keyword INFO
- Check Log Message ${kw.body[6].body[3]} end keyword WARN
- Check Log Message ${kw.body[7].body[0]} start return INFO
- Check Log Message ${kw.body[7].body[1]} start return WARN
- Check Log Message ${kw.body[7].body[2]} end return INFO
- Check Log Message ${kw.body[7].body[3]} end return WARN
- Check Log Message ${kw.body[8]} end ${type} INFO
- Check Log Message ${kw.body[9]} end ${type} WARN
+ Check Log Message ${kw[0]} start ${type} INFO
+ Check Log Message ${kw[1]} start ${type} WARN
+ Check Log Message ${kw[2, 0]} start keyword INFO
+ Check Log Message ${kw[2, 1]} start keyword WARN
+ Check Log Message ${kw[2, 2]} log_message: INFO Hello says "${name}"! INFO
+ Check Log Message ${kw[2, 3]} log_message: INFO Hello says "${name}"! WARN
+ Check Log Message ${kw[2, 4]} Hello says "${name}"! INFO
+ Check Log Message ${kw[2, 5]} end keyword INFO
+ Check Log Message ${kw[2, 6]} end keyword WARN
+ Check Log Message ${kw[3, 0]} start keyword INFO
+ Check Log Message ${kw[3, 1]} start keyword WARN
+ Check Log Message ${kw[3, 2]} end keyword INFO
+ Check Log Message ${kw[3, 3]} end keyword WARN
+ Check Log Message ${kw[4, 0]} start keyword INFO
+ Check Log Message ${kw[4, 1]} start keyword WARN
+ Check Log Message ${kw[4, 2]} log_message: INFO \${assign} = JUST TESTING... INFO
+ Check Log Message ${kw[4, 3]} log_message: INFO \${assign} = JUST TESTING... WARN
+ Check Log Message ${kw[4, 4]} \${assign} = JUST TESTING... INFO
+ Check Log Message ${kw[4, 5]} end keyword INFO
+ Check Log Message ${kw[4, 6]} end keyword WARN
+ Check Log Message ${kw[5, 0]} start var INFO
+ Check Log Message ${kw[5, 1]} start var WARN
+ Check Log Message ${kw[5, 2]} log_message: INFO \${expected} = JUST TESTING... INFO
+ Check Log Message ${kw[5, 3]} log_message: INFO \${expected} = JUST TESTING... WARN
+ Check Log Message ${kw[5, 4]} \${expected} = JUST TESTING... INFO
+ Check Log Message ${kw[5, 5]} end var INFO
+ Check Log Message ${kw[5, 6]} end var WARN
+ Check Log Message ${kw[6, 0]} start keyword INFO
+ Check Log Message ${kw[6, 1]} start keyword WARN
+ Check Log Message ${kw[6, 2]} end keyword INFO
+ Check Log Message ${kw[6, 3]} end keyword WARN
+ Check Log Message ${kw[7, 0]} start return INFO
+ Check Log Message ${kw[7, 1]} start return WARN
+ Check Log Message ${kw[7, 2]} end return INFO
+ Check Log Message ${kw[7, 3]} end return WARN
+ Check Log Message ${kw[8]} end ${type} INFO
+ Check Log Message ${kw[9]} end ${type} WARN
+
+Teardown has correct messages
+ [Arguments] ${teardown}
+ Check Log Message ${teardown[0]} start teardown INFO
+ Check Log Message ${teardown[1]} start teardown WARN
+ Check Log Message ${teardown[2]} log_message: INFO Teardown! INFO
+ Check Log Message ${teardown[3]} log_message: INFO Teardown! WARN
+ Check Log Message ${teardown[4]} Teardown!
+ Check Log Message ${teardown[5]} end teardown INFO
+ Check Log Message ${teardown[6]} end teardown WARN
'Fail' has correct messages
[Arguments] ${kw}
- Check Log Message ${kw.body[0]} start keyword INFO
- Check Log Message ${kw.body[1]} start keyword WARN
- Check Log Message ${kw.body[2]} log_message: FAIL Expected failure INFO
- Check Log Message ${kw.body[3]} log_message: FAIL Expected failure WARN
- Check Log Message ${kw.body[4]} Expected failure FAIL
- Check Log Message ${kw.body[5]} end keyword INFO
- Check Log Message ${kw.body[6]} end keyword WARN
+ Check Log Message ${kw[0]} start keyword INFO
+ Check Log Message ${kw[1]} start keyword WARN
+ Check Log Message ${kw[2]} log_message: FAIL Expected failure INFO
+ Check Log Message ${kw[3]} log_message: FAIL Expected failure WARN
+ Check Log Message ${kw[4]} Expected failure FAIL
+ Check Log Message ${kw[5]} end keyword INFO
+ Check Log Message ${kw[6]} end keyword WARN
Correct messages should be logged to syslog
FOR ${msg} IN
... message: INFO Robot Framework
... start_suite
... end_suite
- ... start_test
- ... end_test
... output_file
... log_file
... report_file
diff --git a/atest/robot/output/listener_interface/listener_methods.robot b/atest/robot/output/listener_interface/listener_methods.robot
index f9ad98177f9..c8dce3cea93 100644
--- a/atest/robot/output/listener_interface/listener_methods.robot
+++ b/atest/robot/output/listener_interface/listener_methods.robot
@@ -47,8 +47,8 @@ Keyword Status
Executing Keywords from Listeners
Run Tests --listener listeners.KeywordExecutingListener misc/pass_and_fail.robot
${tc}= Get Test Case Pass
- Check Log Message ${tc.kws[0].msgs[0]} Start Pass
- Check Log Message ${tc.kws[2].msgs[0]} End Pass
+ Check Log Message ${tc[0, 0]} Start Pass
+ Check Log Message ${tc[4, 0]} End Pass
Test Template
${listener} = Normalize Path ${LISTENER DIR}/verify_template_listener.py
@@ -94,63 +94,80 @@ Check Listen All File
@{expected}= Create List Got settings on level: INFO
... SUITE START: Pass And Fail (s1) 'Some tests here' [ListenerMeta: Hello]
... SETUP START: My Keyword ['Suite Setup'] (line 3)
- ... KEYWORD START: BuiltIn.Log ['Hello says "\${who}"!', '\${LEVEL1}'] (line 27)
+ ... KEYWORD START: BuiltIn.Log ['Hello says "\${who}"!', '\${LEVEL1}'] (line 32)
... LOG MESSAGE: [INFO] Hello says "Suite Setup"!
... KEYWORD END: PASS
- ... KEYWORD START: BuiltIn.Log ['Debug message', '\${LEVEL2}'] (line 28)
+ ... KEYWORD START: BuiltIn.Log ['Debug message', '\${LEVEL2}'] (line 33)
... KEYWORD END: PASS
- ... KEYWORD START: \${assign} = String.Convert To Upper Case ['Just testing...'] (line 29)
+ ... KEYWORD START: \${assign} = String.Convert To Upper Case ['Just testing...'] (line 34)
... LOG MESSAGE: [INFO] \${assign} = JUST TESTING...
... KEYWORD END: PASS
- ... VAR START: \${expected}${SPACE*4}JUST TESTING... (line 30)
+ ... VAR START: \${expected}${SPACE*4}JUST TESTING... (line 35)
+ ... LOG MESSAGE: [INFO] \${expected} = JUST TESTING...
... VAR END: PASS
- ... KEYWORD START: BuiltIn.Should Be Equal ['\${assign}', '\${expected}'] (line 31)
+ ... KEYWORD START: BuiltIn.Should Be Equal ['\${assign}', '\${expected}'] (line 36)
... KEYWORD END: PASS
- ... RETURN START: (line 32)
+ ... RETURN START: (line 37)
... RETURN END: PASS
... SETUP END: PASS
- ... TEST START: Pass (s1-t1, line 12) '' ['force', 'pass']
- ... KEYWORD START: My Keyword ['Pass'] (line 15)
- ... KEYWORD START: BuiltIn.Log ['Hello says "\${who}"!', '\${LEVEL1}'] (line 27)
+ ... TEST START: Pass (s1-t1, line 15) '' ['force', 'pass']
+ ... KEYWORD START: My Keyword ['Pass'] (line 18)
+ ... KEYWORD START: BuiltIn.Log ['Hello says "\${who}"!', '\${LEVEL1}'] (line 32)
... LOG MESSAGE: [INFO] Hello says "Pass"!
... KEYWORD END: PASS
- ... KEYWORD START: BuiltIn.Log ['Debug message', '\${LEVEL2}'] (line 28)
+ ... KEYWORD START: BuiltIn.Log ['Debug message', '\${LEVEL2}'] (line 33)
... KEYWORD END: PASS
- ... KEYWORD START: \${assign} = String.Convert To Upper Case ['Just testing...'] (line 29)
+ ... KEYWORD START: \${assign} = String.Convert To Upper Case ['Just testing...'] (line 34)
... LOG MESSAGE: [INFO] \${assign} = JUST TESTING...
... KEYWORD END: PASS
- ... VAR START: \${expected}${SPACE*4}JUST TESTING... (line 30)
+ ... VAR START: \${expected}${SPACE*4}JUST TESTING... (line 35)
+ ... LOG MESSAGE: [INFO] \${expected} = JUST TESTING...
... VAR END: PASS
- ... KEYWORD START: BuiltIn.Should Be Equal ['\${assign}', '\${expected}'] (line 31)
+ ... KEYWORD START: BuiltIn.Should Be Equal ['\${assign}', '\${expected}'] (line 36)
... KEYWORD END: PASS
- ... RETURN START: (line 32)
+ ... RETURN START: (line 37)
... RETURN END: PASS
... KEYWORD END: PASS
+ ... KEYWORD START: example.Resource Keyword (line 19)
+ ... KEYWORD START: BuiltIn.Log ['Hello, resource!'] (line 3)
+ ... LOG MESSAGE: [INFO] Hello, resource!
+ ... KEYWORD END: PASS
+ ... KEYWORD END: PASS
+ ... KEYWORD START: BuiltIn.Should Be Equal ['\${VARIABLE}', 'From variables.py with arg 1'] (line 20)
+ ... KEYWORD END: PASS
+ ... TEARDOWN START: BuiltIn.Log ['Teardown!'] (line 4)
+ ... LOG MESSAGE: [INFO] Teardown!
+ ... TEARDOWN END: PASS
... TEST END: PASS
- ... TEST START: Fail (s1-t2, line 17) 'FAIL Expected failure' ['fail', 'force']
- ... KEYWORD START: My Keyword ['Fail'] (line 20)
- ... KEYWORD START: BuiltIn.Log ['Hello says "\${who}"!', '\${LEVEL1}'] (line 27)
+ ... TEST START: Fail (s1-t2, line 22) 'FAIL Expected failure' ['fail', 'force']
+ ... KEYWORD START: My Keyword ['Fail'] (line 25)
+ ... KEYWORD START: BuiltIn.Log ['Hello says "\${who}"!', '\${LEVEL1}'] (line 32)
... LOG MESSAGE: [INFO] Hello says "Fail"!
... KEYWORD END: PASS
- ... KEYWORD START: BuiltIn.Log ['Debug message', '\${LEVEL2}'] (line 28)
+ ... KEYWORD START: BuiltIn.Log ['Debug message', '\${LEVEL2}'] (line 33)
... KEYWORD END: PASS
- ... KEYWORD START: \${assign} = String.Convert To Upper Case ['Just testing...'] (line 29)
+ ... KEYWORD START: \${assign} = String.Convert To Upper Case ['Just testing...'] (line 34)
... LOG MESSAGE: [INFO] \${assign} = JUST TESTING...
... KEYWORD END: PASS
- ... VAR START: \${expected}${SPACE*4}JUST TESTING... (line 30)
+ ... VAR START: \${expected}${SPACE*4}JUST TESTING... (line 35)
+ ... LOG MESSAGE: [INFO] \${expected} = JUST TESTING...
... VAR END: PASS
- ... KEYWORD START: BuiltIn.Should Be Equal ['\${assign}', '\${expected}'] (line 31)
+ ... KEYWORD START: BuiltIn.Should Be Equal ['\${assign}', '\${expected}'] (line 36)
... KEYWORD END: PASS
- ... RETURN START: (line 32)
+ ... RETURN START: (line 37)
... RETURN END: PASS
... KEYWORD END: PASS
- ... KEYWORD START: BuiltIn.Fail ['Expected failure'] (line 21)
+ ... KEYWORD START: BuiltIn.Fail ['Expected failure'] (line 26)
... LOG MESSAGE: [FAIL] Expected failure
... KEYWORD END: FAIL
+ ... TEARDOWN START: BuiltIn.Log ['Teardown!'] (line 4)
+ ... LOG MESSAGE: [INFO] Teardown!
+ ... TEARDOWN END: PASS
... TEST END: FAIL Expected failure
... SUITE END: FAIL 2 tests, 1 passed, 1 failed
... Output: output.xml Closing...
Check Listener File ${filename} @{expected}
+ Stderr Should Be Empty
Calling listener failed
[Arguments] ${method} ${error}
diff --git a/atest/robot/output/listener_interface/listener_order.robot b/atest/robot/output/listener_interface/listener_order.robot
new file mode 100644
index 00000000000..162b4a50154
--- /dev/null
+++ b/atest/robot/output/listener_interface/listener_order.robot
@@ -0,0 +1,57 @@
+*** Settings ***
+Suite Setup Run Tests With Ordered Listeners
+Resource atest_resource.robot
+
+*** Variables ***
+${LISTENER} ${DATADIR}/output/listener_interface/ListenerOrder.py
+
+*** Test Cases ***
+Validate normal order
+ VAR ${expected}
+ ... LIB 3 (999.9): start_suite
+ ... CLI 2 (3.14): start_suite
+ ... CLI 3 (None): start_suite
+ ... LIB 1 (0): start_suite
+ ... LIB 2 (None): start_suite
+ ... CLI 1 (-1): start_suite
+ ... LIB 3 (999.9): log_message
+ ... CLI 2 (3.14): log_message
+ ... CLI 3 (None): log_message
+ ... LIB 1 (0): log_message
+ ... LIB 2 (None): log_message
+ ... CLI 1 (-1): log_message
+ ... LIB 3 (999.9): end_test
+ ... CLI 2 (3.14): end_test
+ ... CLI 3 (None): end_test
+ ... LIB 1 (0): end_test
+ ... LIB 2 (None): end_test
+ ... CLI 1 (-1): end_test
+ ... separator=\n
+ File Should Be Equal To %{TEMPDIR}/listener_order.log ${expected}\n
+
+Validate close order
+ [Documentation] Library listeners are closed when libraries go out of scope.
+ VAR ${expected}
+ ... LIB 1 (0): close
+ ... LIB 2 (None): close
+ ... LIB 3 (999.9): close
+ ... CLI 2 (3.14): close
+ ... CLI 3 (None): close
+ ... CLI 1 (-1): close
+ ... separator=\n
+ File Should Be Equal To %{TEMPDIR}/listener_close_order.log ${expected}\n
+
+Invalid priority
+ ${listener} = Normalize Path ${LISTENER}
+ Check Log Message ${ERRORS}[0] Taking listener '${listener}:NOT USED:invalid' into use failed: Invalid listener priority 'invalid'. ERROR
+ Check Log Message ${ERRORS}[1] Error in library 'BAD': Registering listeners failed: Taking listener 'SELF' into use failed: Invalid listener priority 'bad'. ERROR
+
+*** Keywords ***
+Run Tests With Ordered Listeners
+ ${listener} = Normalize Path ${LISTENER}
+ VAR ${options}
+ ... --listener "${listener}:CLI 1:-1"
+ ... --listener "${listener}:CLI 2:3.14"
+ ... --listener "${listener}:NOT USED:invalid"
+ ... --listener "${listener}:CLI 3"
+ Run Tests ${options} output/listener_interface/listener_order.robot
diff --git a/atest/robot/output/listener_interface/listener_v3.robot b/atest/robot/output/listener_interface/listener_v3.robot
index b736343b180..031dd246aaa 100644
--- a/atest/robot/output/listener_interface/listener_v3.robot
+++ b/atest/robot/output/listener_interface/listener_v3.robot
@@ -1,5 +1,5 @@
*** Settings ***
-Suite Setup Run Tests --listener ${LISTENER DIR}/v3.py -l l -r r -b d -x x misc/pass_and_fail.robot
+Suite Setup Run Tests --listener ${LISTENER DIR}/v3.py -l l -r r -b d -x x -L trace misc/pass_and_fail.robot
Resource listener_resource.robot
*** Variables ***
@@ -8,11 +8,11 @@ ${SEPARATOR} ${EMPTY + '-' * 78}
*** Test Cases ***
New tests and keywords can be added
${tc} = Check test case Added by start_suite [start suite] FAIL [start] [end]
- Check keyword data ${tc.kws[0]} BuiltIn.No Operation
+ Check keyword data ${tc[0]} BuiltIn.No Operation
${tc} = Check test case Added by startTest PASS Dynamically added! [end]
- Check keyword data ${tc.kws[0]} BuiltIn.Fail args=Dynamically added! status=FAIL
+ Check keyword data ${tc[0]} BuiltIn.Fail args=Dynamically added! status=FAIL
${tc} = Check test case Added by end_Test FAIL [start] [end]
- Check keyword data ${tc.kws[0]} BuiltIn.Log args=Dynamically added!, INFO
+ Check keyword data ${tc[0]} BuiltIn.Log args=Dynamically added!, INFO
Stdout Should Contain SEPARATOR=\n
... Added by start_suite [start suite] :: [start suite] ${SPACE*17} | FAIL |
... [start] [end]
@@ -63,13 +63,35 @@ Changing current element docs does not change console output, but does change ou
Check Test Doc Pass [start suite] [start suite] [start test] [end test]
Log messages and timestamps can be changed
- ${tc} = Get test case Pass [start suite]
- Check log message ${tc.kws[0].kws[0].msgs[0]} HELLO SAYS "PASS"!
- Should be equal ${tc.kws[0].kws[0].msgs[0].timestamp} ${datetime(2015, 12, 16, 15, 51, 20, 141000)}
+ ${tc} = Get Test Case Pass [start suite]
+ Check Keyword Data ${tc[0, 0]} BuiltIn.Log args=Hello says "\${who}"!, \${LEVEL1}
+ Check Log Message ${tc[0, 0, 0]} HELLO SAYS "PASS"!
+ Should Be Equal ${tc[0, 0, 0].timestamp} ${datetime(2015, 12, 16, 15, 51, 20, 141000)}
+
+Log message can be removed by setting message to `None`
+ ${tc} = Get Test Case Fail [start suite]
+ Check Keyword Data ${tc[0, 0]} BuiltIn.Log args=Hello says "\${who}"!, \${LEVEL1}
+ Should Be Empty ${tc[0, 0].body}
+ File Should Not Contain ${OUTDIR}/d.txt HELLO SAYS "FAIL"!
+ File Should Not Contain ${OUTDIR}/d.txt None
Syslog messages can be changed
Syslog Should Contain Match 2015-12-16 15:51:20.141000 | INFO \ | TESTS EXECUTION ENDED. STATISTICS:
+Library import
+ Stdout Should Contain Imported library 'BuiltIn' with 107 keywords.
+ Stdout Should Contain Imported library 'String' with 32 keywords.
+ ${tc} = Get Test Case Pass [start suite]
+ Check Keyword Data ${tc[0, 0]} BuiltIn.Log doc=Changed! args=Hello says "\${who}"!, \${LEVEL1}
+
+Resource import
+ Stdout Should Contain Imported resource 'example' with 2 keywords.
+ ${tc} = Get Test Case Pass [start suite]
+ Check Keyword Data ${tc[1, 1]} example.New! doc=Dynamically created.
+
+Variables import
+ Stdout Should Contain Imported variables 'variables.py' without much info.
+
File methods and close are called
Stderr Should Be Equal To SEPARATOR=\n
... Debug: d.txt
@@ -78,3 +100,9 @@ File methods and close are called
... Log: l.html
... Report: r.html
... Close\n
+
+File methods when files are disabled
+ Run Tests Without Processing Output --listener ${LISTENER DIR}/v3.py -o NONE -r NONE -l NONE misc/pass_and_fail.robot
+ Stderr Should Be Equal To SEPARATOR=\n
+ ... Output: None
+ ... Close\n
diff --git a/atest/robot/output/listener_interface/log_levels.robot b/atest/robot/output/listener_interface/log_levels.robot
index 56c3a12f050..0cc5d59efe0 100644
--- a/atest/robot/output/listener_interface/log_levels.robot
+++ b/atest/robot/output/listener_interface/log_levels.robot
@@ -11,11 +11,17 @@ Log messages are collected on INFO level by default
Logged messages should be
... INFO: Hello says "Suite Setup"!
... INFO: \${assign} = JUST TESTING...
+ ... INFO: \${expected} = JUST TESTING...
... INFO: Hello says "Pass"!
... INFO: \${assign} = JUST TESTING...
+ ... INFO: \${expected} = JUST TESTING...
+ ... INFO: Hello, resource!
+ ... INFO: Teardown!
... INFO: Hello says "Fail"!
... INFO: \${assign} = JUST TESTING...
+ ... INFO: \${expected} = JUST TESTING...
... FAIL: Expected failure
+ ... INFO: Teardown!
Log messages are collected on specified level
Run Tests -L DEBUG --listener listeners.Messages;${MESSAGE FILE} misc/pass_and_fail.robot
@@ -23,18 +29,26 @@ Log messages are collected on specified level
... INFO: Hello says "Suite Setup"!
... DEBUG: Debug message
... INFO: \${assign} = JUST TESTING...
+ ... INFO: \${expected} = JUST TESTING...
... DEBUG: Argument types are:
...
...
... INFO: Hello says "Pass"!
... DEBUG: Debug message
... INFO: \${assign} = JUST TESTING...
+ ... INFO: \${expected} = JUST TESTING...
... DEBUG: Argument types are:
...
...
+ ... INFO: Hello, resource!
+ ... DEBUG: Argument types are:
+ ...
+ ...
+ ... INFO: Teardown!
... INFO: Hello says "Fail"!
... DEBUG: Debug message
... INFO: \${assign} = JUST TESTING...
+ ... INFO: \${expected} = JUST TESTING...
... DEBUG: Argument types are:
...
...
@@ -42,6 +56,7 @@ Log messages are collected on specified level
... DEBUG: Traceback (most recent call last):
... ${SPACE*2}None
... AssertionError: Expected failure
+ ... INFO: Teardown!
*** Keywords ***
Logged messages should be
diff --git a/atest/robot/output/listener_interface/output_files.robot b/atest/robot/output/listener_interface/output_files.robot
index c03b1afa115..f75323b1188 100644
--- a/atest/robot/output/listener_interface/output_files.robot
+++ b/atest/robot/output/listener_interface/output_files.robot
@@ -1,7 +1,6 @@
*** Settings ***
Documentation Testing that listener gets information about different output files.
... Tests also that the listener can be taken into use with path.
-Suite Setup Run Some Tests
Suite Teardown Remove Listener Files
Resource listener_resource.robot
@@ -9,23 +8,38 @@ Resource listener_resource.robot
${LISTENERS} ${CURDIR}${/}..${/}..${/}..${/}testresources${/}listeners
*** Test Cases ***
-Output Files
- ${file} = Get Listener File ${ALL_FILE}
- ${expected} = Catenate SEPARATOR=\n
+Output files
+ ${options} = Catenate
+ ... --listener "${LISTENERS}${/}ListenAll.py"
+ ... --output myout.xml
+ ... --report myrep.html
+ ... --log mylog.html
+ ... --xunit myxun.xml
+ ... --debugfile mydeb.txt
+ Run Tests ${options} misc/pass_and_fail.robot output=${OUTDIR}/myout.xml
+ Validate result files
... Debug: mydeb.txt
... Output: myout.xml
+ ... Xunit: myxun.xml
... Log: mylog.html
... Report: myrep.html
- ... Closing...\n
- Should End With ${file} ${expected}
-*** Keywords ***
-Run Some Tests
+Output files disabled
${options} = Catenate
- ... --listener "${LISTENERS}${/}ListenAll.py"
- ... --log mylog.html
- ... --report myrep.html
- ... --output myout.xml
- ... --debugfile mydeb.txt
- Run Tests ${options} misc/pass_and_fail.robot output=${OUTDIR}/myout.xml
- Should Be Equal ${SUITE.name} Pass And Fail
+ ... --listener "${LISTENERS}${/}ListenAll.py:output_file_disabled=True"
+ ... --log NONE
+ ... --report NONE
+ ... --output NONE
+ Run Tests Without Processing Output ${options} misc/pass_and_fail.robot
+ Validate result files
+ ... Output: None
+
+*** Keywords ***
+Validate result files
+ [Arguments] @{files}
+ ${file} = Get Listener File ${ALL_FILE}
+ ${expected} = Catenate SEPARATOR=\n
+ ... @{files}
+ ... Closing...\n
+ Should End With ${file} ${expected}
+ Stderr Should Be Empty
diff --git a/atest/robot/output/listener_interface/recursion.robot b/atest/robot/output/listener_interface/recursion.robot
new file mode 100644
index 00000000000..2b951649ca0
--- /dev/null
+++ b/atest/robot/output/listener_interface/recursion.robot
@@ -0,0 +1,41 @@
+*** Settings ***
+Suite Setup Run Tests --listener ${LISTENER DIR}/Recursion.py ${LISTENER DIR}/recursion.robot
+Resource listener_resource.robot
+
+*** Test Cases ***
+Limited recursion in start_keyword, end_keyword and log_message
+ ${tc} = Check Test Case Limited recursion
+ Length Should Be ${tc.body} 1
+ VAR ${kw} ${tc[0]}
+ Check Keyword Data ${kw} BuiltIn.Log args=Limited 3 children=5
+ Check Keyword Data ${kw[0]} BuiltIn.Log args=Limited 2 (by start_keyword) children=4
+ Check Keyword Data ${kw[0, 0]} BuiltIn.Log args=Limited 1 (by start_keyword) children=1
+ Check Log Message ${kw[0, 0, 0]} Limited 1 (by start_keyword)
+ Check Log Message ${kw[0, 1]} Limited 1 (by log_message)
+ Check Log Message ${kw[0, 2]} Limited 2 (by start_keyword)
+ Check Keyword Data ${kw[0, 3]} BuiltIn.Log args=Limited 1 (by end_keyword) children=1
+ Check Log Message ${kw[0, 3, 0]} Limited 1 (by end_keyword)
+ Check Log Message ${kw[1]} Limited 1 (by log_message)
+ Check Log Message ${kw[2]} Limited 2 (by log_message)
+ Check Log Message ${kw[3]} Limited 3
+ Check Keyword Data ${kw[4]} BuiltIn.Log args=Limited 2 (by end_keyword) children=4
+ Check Keyword Data ${kw[4, 0]} BuiltIn.Log args=Limited 1 (by start_keyword) children=1
+ Check Log Message ${kw[4, 0, 0]} Limited 1 (by start_keyword)
+ Check Log Message ${kw[4, 1]} Limited 1 (by log_message)
+ Check Log Message ${kw[4, 2]} Limited 2 (by end_keyword)
+ Check Keyword Data ${kw[4, 3]} BuiltIn.Log args=Limited 1 (by end_keyword) children=1
+ Check Log Message ${kw[4, 3, 0]} Limited 1 (by end_keyword)
+
+Unlimited recursion in start_keyword, end_keyword and log_message
+ Check Test Case Unlimited recursion
+ Check Recursion Error ${ERRORS[0]} start_keyword Recursive execution stopped.
+ Check Recursion Error ${ERRORS[1]} end_keyword Recursive execution stopped.
+ Check Recursion Error ${ERRORS[2]} log_message RecursionError: *
+
+*** Keywords ***
+Check Recursion Error
+ [Arguments] ${msg} ${method} ${error}
+ ${listener} = Normalize Path ${LISTENER DIR}/Recursion.py
+ Check Log Message ${msg}
+ ... Calling method '${method}' of listener '${listener}' failed: ${error}
+ ... ERROR pattern=True
diff --git a/atest/robot/output/listener_interface/result_model.robot b/atest/robot/output/listener_interface/result_model.robot
new file mode 100644
index 00000000000..3f56c5c0472
--- /dev/null
+++ b/atest/robot/output/listener_interface/result_model.robot
@@ -0,0 +1,29 @@
+*** Settings ***
+Suite Setup Run Tests --listener "${LISTENER DIR}/ResultModel.py;${MODEL FILE}" --loglevel DEBUG ${LISTENER DIR}/result_model.robot
+Resource listener_resource.robot
+
+*** Variables ***
+${MODEL FILE} %{TEMPDIR}/listener_result_model.json
+
+*** Test Cases ***
+Result model is consistent with information sent to listeners
+ Should Be Empty ${ERRORS}
+
+Result model build during execution is same as saved to output.xml
+ ${expected} = Check Test Case Test
+ ${actual} = Evaluate robot.result.TestCase.from_json($MODEL_FILE)
+ ${suite} = Evaluate robot.result.TestSuite.from_dict({'tests': [$actual]}) # Required to get correct id.
+ Dictionaries Should Be Equal ${actual.to_dict()} ${expected.to_dict()}
+
+Messages below log level and messages explicitly removed are not included
+ ${tc} = Check Test Case Test
+ Check Keyword Data ${tc[2, 1]} BuiltIn.Log args=User keyword, DEBUG children=3
+ Check Log Message ${tc[2, 1, 0]} Starting KEYWORD
+ Check Log Message ${tc[2, 1, 1]} User keyword DEBUG
+ Check Log Message ${tc[2, 1, 2]} Ending KEYWORD
+ Check Keyword Data ${tc[2, 2]} BuiltIn.Log args=Not logged, TRACE children=2
+ Check Log Message ${tc[2, 2, 0]} Starting KEYWORD
+ Check Log Message ${tc[2, 2, 1]} Ending KEYWORD
+ Check Keyword Data ${tc[2, 3]} BuiltIn.Log args=Remove me! children=2
+ Check Log Message ${tc[2, 3, 0]} Starting KEYWORD
+ Check Log Message ${tc[2, 3, 1]} Ending KEYWORD
diff --git a/atest/robot/output/listener_interface/using_run_keyword.robot b/atest/robot/output/listener_interface/using_run_keyword.robot
index 59e78a46744..be7635fe20a 100644
--- a/atest/robot/output/listener_interface/using_run_keyword.robot
+++ b/atest/robot/output/listener_interface/using_run_keyword.robot
@@ -4,179 +4,289 @@ Resource listener_resource.robot
*** Test Cases ***
In start_suite when suite has no setup
- Should Be Equal ${SUITE.setup.full_name} Implicit setup
- Should Be Equal ${SUITE.setup.body[0].full_name} BuiltIn.Log
- Check Log Message ${SUITE.setup.body[0].body[0]} start_suite
- Length Should Be ${SUITE.setup.body} 1
+ Check Keyword Data ${SUITE.setup} Implicit setup type=SETUP children=1
+ Validate Log ${SUITE.setup[0]} start_suite
In end_suite when suite has no teardown
- Should Be Equal ${SUITE.teardown.full_name} Implicit teardown
- Should Be Equal ${SUITE.teardown.body[0].full_name} BuiltIn.Log
- Check Log Message ${SUITE.teardown.body[0].body[0]} end_suite
- Length Should Be ${SUITE.teardown.body} 1
+ Check Keyword Data ${SUITE.teardown} Implicit teardown type=TEARDOWN children=1
+ Validate Log ${SUITE.teardown[0]} end_suite
In start_suite when suite has setup
- ${suite} = Set Variable ${SUITE.suites[1]}
- Should Be Equal ${suite.setup.full_name} Suite Setup
- Should Be Equal ${suite.setup.body[0].full_name} BuiltIn.Log
- Check Log Message ${suite.setup.body[0].body[0]} start_suite
- Length Should Be ${suite.setup.body} 5
+ VAR ${kw} ${SUITE.suites[1].setup}
+ Check Keyword Data ${kw} Suite Setup type=SETUP children=5
+ Validate Log ${kw[0]} start_suite
+ Check Keyword Data ${kw[1]} BuiltIn.Log args=start_keyword children=1
+ Check Log Message ${kw[1, 0]} start_keyword
+ Validate Log ${kw[2]} Keyword
+ Check Keyword Data ${kw[3]} Keyword children=3
+ Check Keyword Data ${kw[3, 0]} BuiltIn.Log args=start_keyword children=1
+ Check Log Message ${kw[3, 0, 0]} start_keyword
+ Check Keyword Data ${kw[3, 1]} BuiltIn.Log args=Keyword children=3
+ Check Keyword Data ${kw[3, 2]} BuiltIn.Log args=end_keyword children=1
+ Check Log Message ${kw[3, 2, 0]} end_keyword
+ Check Keyword Data ${kw[4]} BuiltIn.Log args=end_keyword children=1
+ Check Log Message ${kw[4, 0]} end_keyword
In end_suite when suite has teardown
- ${suite} = Set Variable ${SUITE.suites[1]}
- Should Be Equal ${suite.teardown.full_name} Suite Teardown
- Should Be Equal ${suite.teardown.body[-1].full_name} BuiltIn.Log
- Check Log Message ${suite.teardown.body[-1].body[0]} end_suite
- Length Should Be ${suite.teardown.body} 5
+ VAR ${kw} ${SUITE.suites[1].teardown}
+ Check Keyword Data ${kw} Suite Teardown type=TEARDOWN children=5
+ Check Keyword Data ${kw[0]} BuiltIn.Log args=start_keyword children=1
+ Check Log Message ${kw[0, 0]} start_keyword
+ Validate Log ${kw[1]} Keyword
+ Check Keyword Data ${kw[2]} Keyword children=3
+ Check Keyword Data ${kw[2, 0]} BuiltIn.Log args=start_keyword children=1
+ Check Log Message ${kw[2, 0, 0]} start_keyword
+ Check Keyword Data ${kw[2, 1]} BuiltIn.Log args=Keyword children=3
+ Check Keyword Data ${kw[2, 2]} BuiltIn.Log args=end_keyword children=1
+ Check Log Message ${kw[2, 2, 0]} end_keyword
+ Check Keyword Data ${kw[3]} BuiltIn.Log args=end_keyword children=1
+ Check Log Message ${kw[3, 0]} end_keyword
+ Validate Log ${kw[4]} end_suite
In start_test and end_test when test has no setup or teardown
- ${tc} = Check Test Case First One
- Should Be Equal ${tc.body[0].full_name} BuiltIn.Log
- Check Log Message ${tc.body[0].body[0]} start_test
- Should Be Equal ${tc.body[-1].full_name} BuiltIn.Log
- Check Log Message ${tc.body[-1].body[0]} end_test
- Length Should Be ${tc.body} 5
+ ${tc} = Check Test Case First One
+ Length Should Be ${tc.body} 5
Should Not Be True ${tc.setup}
Should Not Be True ${tc.teardown}
+ Validate Log ${tc[0]} start_test
+ Validate Log ${tc[1]} Test 1
+ Validate Log ${tc[2]} Logging with debug level DEBUG
+ Check Keyword Data ${tc[3]} logs on trace tags=kw, tags children=3
+ Check Keyword Data ${tc[3, 0]} BuiltIn.Log args=start_keyword children=1
+ Check Keyword Data ${tc[3, 1]} BuiltIn.Log args=Log on \${TEST NAME}, TRACE children=3
+ Check Keyword Data ${tc[3, 2]} BuiltIn.Log args=end_keyword children=1
+ Validate Log ${tc[4]} end_test
In start_test and end_test when test has setup and teardown
- ${tc} = Check Test Case Test with setup and teardown
- Should Be Equal ${tc.body[0].full_name} BuiltIn.Log
- Check Log Message ${tc.body[0].body[0]} start_test
- Should Be Equal ${tc.body[-1].full_name} BuiltIn.Log
- Check Log Message ${tc.body[-1].body[0]} end_test
- Length Should Be ${tc.body} 3
- Should Be Equal ${tc.setup.full_name} Test Setup
- Should Be Equal ${tc.teardown.full_name} Test Teardown
+ ${tc} = Check Test Case Test with setup and teardown
+ Length Should Be ${tc.body} 3
+ Check Keyword Data ${tc.setup} Test Setup type=SETUP children=4
+ Check Keyword Data ${tc.teardown} Test Teardown type=TEARDOWN children=4
+ Validate Log ${tc[0]} start_test
+ Check Keyword Data ${tc[1]} Keyword children=3
+ Check Keyword Data ${tc[1, 0]} BuiltIn.Log args=start_keyword children=1
+ Check Log Message ${tc[1, 0, 0]} start_keyword
+ Check Keyword Data ${tc[1, 1]} BuiltIn.Log args=Keyword children=3
+ Check Keyword Data ${tc[1, 2]} BuiltIn.Log args=end_keyword children=1
+ Check Log Message ${tc[1, 2, 0]} end_keyword
+ Validate Log ${tc[2]} end_test
In start_keyword and end_keyword with library keyword
- ${tc} = Check Test Case First One
- Should Be Equal ${tc.body[1].full_name} BuiltIn.Log
- Should Be Equal ${tc.body[1].body[0].full_name} BuiltIn.Log
- Check Log Message ${tc.body[1].body[0].body[0]} start_keyword
- Check Log Message ${tc.body[1].body[1]} Test 1
- Should Be Equal ${tc.body[1].body[2].full_name} BuiltIn.Log
- Check Log Message ${tc.body[1].body[2].body[0]} end_keyword
- Length Should Be ${tc.body[1].body} 3
+ ${tc} = Check Test Case First One
+ Should Be Equal ${tc[1].full_name} BuiltIn.Log
+ Should Be Equal ${tc[1, 0].full_name} BuiltIn.Log
+ Check Log Message ${tc[1, 0, 0]} start_keyword
+ Check Log Message ${tc[1, 1]} Test 1
+ Should Be Equal ${tc[1, 2].full_name} BuiltIn.Log
+ Check Log Message ${tc[1, 2, 0]} end_keyword
+ Length Should Be ${tc[1].body} 3
In start_keyword and end_keyword with user keyword
- ${tc} = Check Test Case First One
- Should Be Equal ${tc.body[3].full_name} logs on trace
- Should Be Equal ${tc.body[3].body[0].full_name} BuiltIn.Log
- Check Log Message ${tc.body[3].body[0].body[0]} start_keyword
- Should Be Equal ${tc.body[3].body[1].full_name} BuiltIn.Log
- Should Be Equal ${tc.body[3].body[1].body[0].full_name} BuiltIn.Log
- Check Log Message ${tc.body[3].body[1].body[0].body[0]} start_keyword
- Should Be Equal ${tc.body[3].body[1].body[1].full_name} BuiltIn.Log
- Check Log Message ${tc.body[3].body[1].body[1].body[0]} end_keyword
- Length Should Be ${tc.body[3].body[1].body} 2
- Should Be Equal ${tc.body[3].body[2].full_name} BuiltIn.Log
- Check Log Message ${tc.body[3].body[2].body[0]} end_keyword
- Length Should Be ${tc.body[3].body} 3
+ ${tc} = Check Test Case First One
+ Should Be Equal ${tc[3].full_name} logs on trace
+ Should Be Equal ${tc[3, 0].full_name} BuiltIn.Log
+ Check Log Message ${tc[3, 0, 0]} start_keyword
+ Should Be Equal ${tc[3, 1].full_name} BuiltIn.Log
+ Should Be Equal ${tc[3, 1, 0].full_name} BuiltIn.Log
+ Check Log Message ${tc[3, 1, 0, 1]} start_keyword
+ Should Be Equal ${tc[3, 1, 2].full_name} BuiltIn.Log
+ Check Log Message ${tc[3, 1, 2, 1]} end_keyword
+ Length Should Be ${tc[3, 1].body} 3
+ Should Be Equal ${tc[3, 2].full_name} BuiltIn.Log
+ Check Log Message ${tc[3, 2, 0]} end_keyword
+ Length Should Be ${tc[3].body} 3
In start_keyword and end_keyword with FOR loop
- ${tc} = Check Test Case FOR
- ${for} = Set Variable ${tc.body[1]}
- Should Be Equal ${for.type} FOR
- Length Should Be ${for.body} 5
- Length Should Be ${for.body.filter(keywords=True)} 2
- Should Be Equal ${for.body[0].full_name} BuiltIn.Log
- Check Log Message ${for.body[0].body[0]} start_keyword
- Should Be Equal ${for.body[-1].full_name} BuiltIn.Log
- Check Log Message ${for.body[-1].body[0]} end_keyword
+ ${tc} = Check Test Case FOR
+ ${for} = Set Variable ${tc[1]}
+ Should Be Equal ${for.type} FOR
+ Length Should Be ${for.body} 5
+ Length Should Be ${for.keywords} 2
+ Should Be Equal ${for[0].full_name} BuiltIn.Log
+ Check Log Message ${for[0, 0]} start_keyword
+ Should Be Equal ${for[-1].full_name} BuiltIn.Log
+ Check Log Message ${for[-1,0]} end_keyword
In start_keyword and end_keyword with WHILE
- ${tc} = Check Test Case While loop executed multiple times
- ${while} = Set Variable ${tc.body[2]}
- Should Be Equal ${while.type} WHILE
- Length Should Be ${while.body} 7
- Length Should Be ${while.body.filter(keywords=True)} 2
- Should Be Equal ${while.body[0].full_name} BuiltIn.Log
- Check Log Message ${while.body[0].body[0]} start_keyword
- Should Be Equal ${while.body[-1].full_name} BuiltIn.Log
- Check Log Message ${while.body[-1].body[0]} end_keyword
-
- In start_keyword and end_keyword with IF/ELSE
- ${tc} = Check Test Case IF structure
- Should Be Equal ${tc.body[1].type} VAR
- Should Be Equal ${tc.body[2].type} IF/ELSE ROOT
- Length Should Be ${tc.body[2].body} 3 # Listener is not called with root
- Validate IF branch ${tc.body[2].body[0]} IF NOT RUN # but is called with unexecuted branches.
- Validate IF branch ${tc.body[2].body[1]} ELSE IF PASS
- Validate IF branch ${tc.body[2].body[2]} ELSE NOT RUN
+ ${tc} = Check Test Case While loop executed multiple times
+ ${while} = Set Variable ${tc[2]}
+ Should Be Equal ${while.type} WHILE
+ Length Should Be ${while.body} 7
+ Length Should Be ${while.keywords} 2
+ Should Be Equal ${while[0].full_name} BuiltIn.Log
+ Check Log Message ${while[0, 0]} start_keyword
+ Should Be Equal ${while[-1].full_name} BuiltIn.Log
+ Check Log Message ${while[-1,0]} end_keyword
+
+In start_keyword and end_keyword with IF/ELSE
+ ${tc} = Check Test Case IF structure
+ Should Be Equal ${tc[1].type} VAR
+ Should Be Equal ${tc[2].type} IF/ELSE ROOT
+ Length Should Be ${tc[2].body} 3 # Listener is not called with root
+ Validate IF branch ${tc[2, 0]} IF NOT RUN # but is called with unexecuted branches.
+ Validate IF branch ${tc[2, 1]} ELSE IF PASS
+ Validate IF branch ${tc[2, 2]} ELSE NOT RUN
In start_keyword and end_keyword with TRY/EXCEPT
- ${tc} = Check Test Case Everything
- Should Be Equal ${tc.body[1].type} TRY/EXCEPT ROOT
- Length Should Be ${tc.body[1].body} 5 # Listener is not called with root
- Validate FOR branch ${tc.body[1].body[0]} TRY FAIL
- Validate FOR branch ${tc.body[1].body[1]} EXCEPT NOT RUN # but is called with unexecuted branches.
- Validate FOR branch ${tc.body[1].body[2]} EXCEPT PASS
- Validate FOR branch ${tc.body[1].body[3]} ELSE NOT RUN
- Validate FOR branch ${tc.body[1].body[4]} FINALLY PASS
+ ${tc} = Check Test Case Everything
+ Should Be Equal ${tc[1].type} TRY/EXCEPT ROOT
+ Length Should Be ${tc[1].body} 5 # Listener is not called with root
+ Validate FOR branch ${tc[1, 0]} TRY FAIL
+ Validate FOR branch ${tc[1, 1]} EXCEPT NOT RUN # but is called with unexecuted branches.
+ Validate FOR branch ${tc[1, 2]} EXCEPT PASS
+ Validate FOR branch ${tc[1, 3]} ELSE NOT RUN
+ Validate FOR branch ${tc[1, 4]} FINALLY PASS
In start_keyword and end_keyword with BREAK and CONTINUE
- ${tc} = Check Test Case WHILE loop in keyword
- FOR ${iter} IN @{tc.body[1].body[2].body[1:-1]}
- Should Be Equal ${iter.body[3].body[0].body[1].type} CONTINUE
- Should Be Equal ${iter.body[3].body[0].body[1].body[0].full_name} BuiltIn.Log
- Check Log Message ${iter.body[3].body[0].body[1].body[0].body[0]} start_keyword
- Should Be Equal ${iter.body[3].body[0].body[1].body[1].full_name} BuiltIn.Log
- Check Log Message ${iter.body[3].body[0].body[1].body[1].body[0]} end_keyword
- Should Be Equal ${iter.body[4].body[0].body[1].type} BREAK
- Should Be Equal ${iter.body[4].body[0].body[1].body[0].full_name} BuiltIn.Log
- Check Log Message ${iter.body[4].body[0].body[1].body[0].body[0]} start_keyword
- Should Be Equal ${iter.body[4].body[0].body[1].body[1].full_name} BuiltIn.Log
- Check Log Message ${iter.body[4].body[0].body[1].body[1].body[0]} end_keyword
+ ${tc} = Check Test Case WHILE loop in keyword
+ FOR ${iter} IN @{tc[1, 2][1:-1]}
+ Should Be Equal ${iter[3, 0, 1].type} CONTINUE
+ Should Be Equal ${iter[3, 0, 1, 0].full_name} BuiltIn.Log
+ Check Log Message ${iter[3, 0, 1, 0, 0]} start_keyword
+ Should Be Equal ${iter[3, 0, 1, 1].full_name} BuiltIn.Log
+ Check Log Message ${iter[3, 0, 1, 1, 0]} end_keyword
+ Should Be Equal ${iter[4, 0, 1].type} BREAK
+ Should Be Equal ${iter[4, 0, 1, 0].full_name} BuiltIn.Log
+ Check Log Message ${iter[4, 0, 1, 0, 0]} start_keyword
+ Should Be Equal ${iter[4, 0, 1, 1].full_name} BuiltIn.Log
+ Check Log Message ${iter[4, 0, 1, 1, 0]} end_keyword
END
In start_keyword and end_keyword with RETURN
- ${tc} = Check Test Case Second One
- Should Be Equal ${tc.body[3].body[1].body[1].body[2].type} RETURN
- Should Be Equal ${tc.body[3].body[1].body[1].body[2].body[0].full_name} BuiltIn.Log
- Check Log Message ${tc.body[3].body[1].body[1].body[2].body[0].body[0]} start_keyword
- Should Be Equal ${tc.body[3].body[1].body[1].body[2].body[1].full_name} BuiltIn.Log
- Check Log Message ${tc.body[3].body[1].body[1].body[2].body[1].body[0]} end_keyword
+ ${tc} = Check Test Case Second One
+ Should Be Equal ${tc[3, 1, 1, 2].type} RETURN
+ Should Be Equal ${tc[3, 1, 1, 2, 0].full_name} BuiltIn.Log
+ Check Log Message ${tc[3, 1, 1, 2, 0, 1]} start_keyword
+ Should Be Equal ${tc[3, 1, 1, 2, 1].full_name} BuiltIn.Log
+ Check Log Message ${tc[3, 1, 1, 2, 1, 1]} end_keyword
+
+With JSON output
+ [Documentation] Mainly test that executed keywords don't cause problems.
+ ...
+ ... Some data, such as keywords and messages on suite level,
+ ... are discarded and thus the exact output isn't the same as
+ ... with XML.
+ ...
+ ... Cannot validate output, because it doesn't match the schema.
+ Run Tests With Keyword Running Listener format=json validate=False
+ Should Contain Tests ${SUITE}
+ ... First One
+ ... Second One
+ ... Test with setup and teardown
+ ... Test with failing setup
+ ... Test with failing teardown
+ ... Failing test with failing teardown
+ ... FOR
+ ... FOR IN RANGE
+ ... FOR IN ENUMERATE
+ ... FOR IN ZIP
+ ... WHILE loop executed multiple times
+ ... WHILE loop in keyword
+ ... IF structure
+ ... Everything
+ ... Library keyword
+ ... User keyword and RETURN
+ ... Test documentation, tags and timeout
+ ... Test setup and teardown
+ ... Keyword Keyword documentation, tags and timeout
+ ... Keyword setup and teardown
+ ... Failure
+ ... VAR
+ ... IF
+ ... TRY
+ ... FOR and CONTINUE
+ ... WHILE and BREAK
+ ... GROUP
+ ... Syntax error
+
+In dry-run
+ Run Tests With Keyword Running Listener --dry-run
+ Should Contain Tests ${SUITE}
+ ... First One
+ ... Test with setup and teardown
+ ... FOR
+ ... FOR IN ENUMERATE
+ ... FOR IN ZIP
+ ... WHILE loop executed multiple times
+ ... WHILE loop in keyword
+ ... IF structure
+ ... Everything
+ ... Library keyword
+ ... User keyword and RETURN
+ ... Test documentation, tags and timeout
+ ... Test setup and teardown
+ ... Keyword Keyword documentation, tags and timeout
+ ... Keyword setup and teardown
+ ... VAR
+ ... IF
+ ... TRY
+ ... FOR and CONTINUE
+ ... WHILE and BREAK
+ ... GROUP
+ ... Second One=FAIL:Several failures occurred:\n\n1) No keyword with name 'Not executed' found.\n\n2) No keyword with name 'Not executed' found.
+ ... Test with failing setup=PASS
+ ... Test with failing teardown=PASS
+ ... Failing test with failing teardown=PASS
+ ... FOR IN RANGE=FAIL:No keyword with name 'Not executed!' found.
+ ... Failure=PASS
+ ... Syntax error=FAIL:Several failures occurred:\n\n1) Non-existing setting 'Bad'.\n\n2) Non-existing setting 'Ooops'.
*** Keywords ***
Run Tests With Keyword Running Listener
- ${path} = Normalize Path ${LISTENER DIR}/keyword_running_listener.py
- ${files} = Catenate
+ [Arguments] ${options}= ${format}=xml ${validate}=True
+ VAR ${listener} ${LISTENER DIR}/keyword_running_listener.py
+ VAR ${output} ${OUTDIR}/output.${format}
+ VAR ${files}
... misc/normal.robot
... misc/setups_and_teardowns.robot
... misc/for_loops.robot
... misc/while.robot
... misc/if_else.robot
... misc/try_except.robot
- Run Tests --listener ${path} ${files} validate output=True
- Should Be Empty ${ERRORS}
+ ... misc/everything.robot
+ Run Tests --listener ${listener} ${options} -L debug -o ${output} ${files} output=${output} validate output=${validate}
+ Length Should Be ${ERRORS} 1
+
+Validate Log
+ [Arguments] ${kw} ${message} ${level}=INFO
+ IF $level == 'INFO'
+ VAR ${args} ${message}
+ ELSE
+ VAR ${args} ${message}, ${level}
+ END
+ Check Keyword Data ${kw} BuiltIn.Log args=${args} children=3
+ Check Keyword Data ${kw[0]} BuiltIn.Log args=start_keyword children=1
+ Check Log Message ${kw[0, 0]} start_keyword
+ Check Log Message ${kw[1]} ${message} ${level}
+ Check Keyword Data ${kw[2]} BuiltIn.Log args=end_keyword children=1
+ Check Log Message ${kw[2, 0]} end_keyword
Validate IF branch
[Arguments] ${branch} ${type} ${status}
- Should Be Equal ${branch.type} ${type}
- Should Be Equal ${branch.status} ${status}
- Length Should Be ${branch.body} 3
- Should Be Equal ${branch.body[0].full_name} BuiltIn.Log
- Check Log Message ${branch.body[0].body[0]} start_keyword
+ Should Be Equal ${branch.type} ${type}
+ Should Be Equal ${branch.status} ${status}
+ Length Should Be ${branch.body} 3
+ Should Be Equal ${branch[0].full_name} BuiltIn.Log
+ Check Log Message ${branch[0, 0]} start_keyword
IF $status == 'PASS'
- Should Be Equal ${branch.body[1].full_name} BuiltIn.Log
- Should Be Equal ${branch.body[1].body[0].full_name} BuiltIn.Log
- Check Log Message ${branch.body[1].body[0].body[0]} start_keyword
- Check Log Message ${branch.body[1].body[1]} else if branch
- Should Be Equal ${branch.body[1].body[2].full_name} BuiltIn.Log
- Check Log Message ${branch.body[1].body[2].body[0]} end_keyword
+ Should Be Equal ${branch[1].full_name} BuiltIn.Log
+ Should Be Equal ${branch[1, 0].full_name} BuiltIn.Log
+ Check Log Message ${branch[1, 0, 0]} start_keyword
+ Check Log Message ${branch[1, 1]} else if branch
+ Should Be Equal ${branch[1, 2].full_name} BuiltIn.Log
+ Check Log Message ${branch[1, 2, 0]} end_keyword
ELSE
- Should Be Equal ${branch.body[1].full_name} BuiltIn.Fail
- Should Be Equal ${branch.body[1].status} NOT RUN
+ Should Be Equal ${branch[1].full_name} BuiltIn.Fail
+ Should Be Equal ${branch[1].status} NOT RUN
END
- Should Be Equal ${branch.body[-1].full_name} BuiltIn.Log
- Check Log Message ${branch.body[-1].body[0]} end_keyword
+ Should Be Equal ${branch[-1].full_name} BuiltIn.Log
+ Check Log Message ${branch[-1,0]} end_keyword
Validate FOR branch
[Arguments] ${branch} ${type} ${status}
- Should Be Equal ${branch.type} ${type}
- Should Be Equal ${branch.status} ${status}
- Should Be Equal ${branch.body[0].full_name} BuiltIn.Log
- Check Log Message ${branch.body[0].body[0]} start_keyword
- Should Be Equal ${branch.body[-1].full_name} BuiltIn.Log
- Check Log Message ${branch.body[-1].body[0]} end_keyword
+ Should Be Equal ${branch.type} ${type}
+ Should Be Equal ${branch.status} ${status}
+ Should Be Equal ${branch[0].full_name} BuiltIn.Log
+ Check Log Message ${branch[0, 0]} start_keyword
+ Should Be Equal ${branch[-1].full_name} BuiltIn.Log
+ Check Log Message ${branch[-1,0]} end_keyword
diff --git a/atest/robot/output/names_needing_escaping.robot b/atest/robot/output/names_needing_escaping.robot
index b7942b154c8..50006670bf3 100644
--- a/atest/robot/output/names_needing_escaping.robot
+++ b/atest/robot/output/names_needing_escaping.robot
@@ -34,4 +34,4 @@ Check TC And UK Name
[Arguments] ${name}
${tc} = Check Test Case ${name}
Should Be Equal ${tc.name} ${name}
- Should Be Equal ${tc.kws[0].name} ${name}
+ Should Be Equal ${tc[0].name} ${name}
diff --git a/atest/robot/output/xunit.robot b/atest/robot/output/xunit.robot
index e0e19be7099..25843cde935 100644
--- a/atest/robot/output/xunit.robot
+++ b/atest/robot/output/xunit.robot
@@ -29,7 +29,7 @@ File Structure Is Correct
${skips} = Get XUnit Nodes testcase/skipped
Length Should Be ${skips} 1
Element Attribute Should Be ${skips}[0] message
- ... Test failed but skip-on-failure mode was active and it was marked skipped.\n\nOriginal failure:\n${MESSAGES}
+ ... Failed test skipped using 'täg' tag.\n\nOriginal failure:\n${MESSAGES}
Element Attribute Should Be ${skips}[0] type SkipExecution
Element Should Not Exist ${root} testsuite/properties
diff --git a/atest/robot/parsing/caching_libs_and_resources.robot b/atest/robot/parsing/caching_libs_and_resources.robot
index eed9f7c46de..4a543f8a3d0 100644
--- a/atest/robot/parsing/caching_libs_and_resources.robot
+++ b/atest/robot/parsing/caching_libs_and_resources.robot
@@ -18,16 +18,16 @@ Process Resource Files Only Once
[Setup] Run Tests And Set $SYSLOG parsing/resource_parsing
# Check that tests are run ok
${tc} = Check Test Case Test 1.1
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} variable value from 02 resource
- Check Log Message ${tc.kws[1].msgs[0]} variable value from 02 resource
+ Check Log Message ${tc[0, 0, 0]} variable value from 02 resource
+ Check Log Message ${tc[1, 0]} variable value from 02 resource
${tc} = Check Test Case Test 4.1
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} variable value from 02 resource
- Check Log Message ${tc.kws[1].msgs[0]} variable value from 02 resource
+ Check Log Message ${tc[0, 0, 0]} variable value from 02 resource
+ Check Log Message ${tc[1, 0]} variable value from 02 resource
${tc} = Check Test Case Test 4.2
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} variable value from 03 resource
- Check Log Message ${tc.kws[0].kws[1].msgs[0]} variable value from 02 resource
- Check Log Message ${tc.kws[0].kws[2].kws[0].msgs[0]} variable value from 02 resource
- Check Log Message ${tc.kws[1].msgs[0]} variable value from 03 resource
+ Check Log Message ${tc[0, 0, 0]} variable value from 03 resource
+ Check Log Message ${tc[0, 1, 0]} variable value from 02 resource
+ Check Log Message ${tc[0, 2, 0, 0]} variable value from 02 resource
+ Check Log Message ${tc[1, 0]} variable value from 03 resource
${dir} = Normalize Path ${DATADIR}/parsing/resource_parsing
Should Contain X Times ${SYSLOG} Parsing file '${dir}${/}02_resource.robot' 1
Should Contain X Times ${SYSLOG} Parsing resource file '${dir}${/}02_resource.robot' 1
diff --git a/atest/robot/parsing/custom_parsers.robot b/atest/robot/parsing/custom_parsers.robot
index cc8bccad2ae..cd720142ee9 100644
--- a/atest/robot/parsing/custom_parsers.robot
+++ b/atest/robot/parsing/custom_parsers.robot
@@ -108,11 +108,11 @@ Validate Directory Suite
... Test in Robot file=PASS
FOR ${test} IN @{SUITE.all_tests}
IF ${init}
- Should Contain Tags ${test} tag from init
- Should Be Equal ${test.timeout} 42 seconds
+ Should Have Tags ${test} tag from init
+ Should Be Equal ${test.timeout} 42 seconds
IF '${test.name}' != 'Empty'
- Check Log Message ${test.setup.msgs[0]} setup from init
- Check Log Message ${test.teardown.msgs[0]} teardown from init
+ Check Log Message ${test.setup[0]} setup from init
+ Check Log Message ${test.teardown[0]} teardown from init
END
ELSE
Should Not Be True ${test.tags}
diff --git a/atest/robot/parsing/data_formats/formats_resource.robot b/atest/robot/parsing/data_formats/formats_resource.robot
index 10463f0ca6e..b1e12ed467d 100644
--- a/atest/robot/parsing/data_formats/formats_resource.robot
+++ b/atest/robot/parsing/data_formats/formats_resource.robot
@@ -33,7 +33,7 @@ Run Sample File And Check Tests
${test} = Check Test Case Test Timeout
Should Be Equal ${test.timeout} 10 milliseconds
${test} = Check Test Case Keyword Timeout
- Should Be Equal ${test.kws[0].timeout} 2 milliseconds
+ Should Be Equal ${test[0].timeout} 2 milliseconds
Check Test Doc Document Testing the metadata parsing.
${test} = Check Test Case Default Fixture
Setup Should Not Be Defined ${test}
@@ -62,7 +62,7 @@ Check Suite With Init
[Arguments] ${suite}
Should Be Equal ${suite.name} With Init
Should Be Equal ${suite.doc} Testing suite init file
- Check Log Message ${suite.setup.kws[0].messages[0]} Running suite setup
+ Check Log Message ${suite.setup[0].messages[0]} Running suite setup
Teardown Should Not Be Defined ${suite}
Should Contain Suites ${suite} Sub Suite1 Sub Suite2
Should Contain Tests ${suite} @{SUBSUITE_TESTS}
diff --git a/atest/robot/parsing/data_formats/resource_extensions.robot b/atest/robot/parsing/data_formats/resource_extensions.robot
index dcc66e3e9f4..e168ea65964 100644
--- a/atest/robot/parsing/data_formats/resource_extensions.robot
+++ b/atest/robot/parsing/data_formats/resource_extensions.robot
@@ -5,11 +5,11 @@ Resource atest_resource.robot
*** Test Cases ***
Resource with '*.resource' extension
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].kws[1].msgs[0]} nested.resource
- Check Log Message ${tc.kws[0].kws[3].msgs[0]} resource.resource
- Check Log Message ${tc.kws[1].kws[1].msgs[0]} nested.resource
- Check Log Message ${tc.kws[4].msgs[0]} resource.resource
- Check Log Message ${tc.kws[5].msgs[0]} nested.resource
+ Check Log Message ${tc[0, 0, 1, 0]} nested.resource
+ Check Log Message ${tc[0, 3, 0]} resource.resource
+ Check Log Message ${tc[1, 1, 0]} nested.resource
+ Check Log Message ${tc[4, 0]} resource.resource
+ Check Log Message ${tc[5, 0]} nested.resource
'*.resource' files are not parsed for tests
Should Contain Suites ${SUITE} Tests
diff --git a/atest/robot/parsing/ignore_bom.robot b/atest/robot/parsing/ignore_bom.robot
index c8bb2b799a6..6897f32c885 100644
--- a/atest/robot/parsing/ignore_bom.robot
+++ b/atest/robot/parsing/ignore_bom.robot
@@ -7,12 +7,12 @@ Resource atest_resource.robot
Byte order mark in plain text file
[Setup] File Should Have Bom parsing/bom.robot
${tc} = Check test case ${TESTNAME}
- Check log message ${tc.kws[0].msgs[0]} Hyvää päivää €åppa!
+ Check log message ${tc[0, 0]} Hyvää päivää €åppa!
Byte order mark in TSV file
[Setup] File Should Have Bom parsing/bom.robot
${tc} = Check test case ${TESTNAME}
- Check log message ${tc.kws[0].msgs[0]} Hyvää päivää €åppa!
+ Check log message ${tc[0, 0]} Hyvää päivää €åppa!
*** Keywords ***
File Should Have Bom
diff --git a/atest/robot/parsing/line_continuation.robot b/atest/robot/parsing/line_continuation.robot
index 5ced4ba04b5..2a06b84067d 100644
--- a/atest/robot/parsing/line_continuation.robot
+++ b/atest/robot/parsing/line_continuation.robot
@@ -8,13 +8,13 @@ Multiline suite documentation and metadata
Should Be Equal ${SUITE.metadata['Name']} 1.1\n1.2\n\n2.1\n2.2\n2.3\n\n3.1
Multiline suite level settings
- Should Contain Tags ${SUITE.tests[0]}
+ Should Have Tags ${SUITE.tests[0]}
... ... t1 t2 t3 t4 t5 t6 t7 t8 t9
- Check Log Message ${SUITE.tests[0].teardown.msgs[0]} 1st
- Check Log Message ${SUITE.tests[0].teardown.msgs[1]} ${EMPTY}
- Check Log Message ${SUITE.tests[0].teardown.msgs[2]} 2nd last
- Check Log Message ${SUITE.tests[0].teardown.msgs[3]} ${EMPTY}
- Length Should Be ${SUITE.tests[0].teardown.msgs} 4
+ Check Log Message ${SUITE.tests[0].teardown[0]} 1st
+ Check Log Message ${SUITE.tests[0].teardown[1]} ${EMPTY}
+ Check Log Message ${SUITE.tests[0].teardown[2]} 2nd last
+ Check Log Message ${SUITE.tests[0].teardown[3]} ${EMPTY}
+ Length Should Be ${SUITE.tests[0].teardown.body} 4
Multiline import
Check Test Case ${TEST NAME}
@@ -24,21 +24,21 @@ Multiline variables
Multiline arguments with library keyword
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} one
- Check Log Message ${tc.kws[0].msgs[1]} two
- Check Log Message ${tc.kws[0].msgs[2]} three
- Check Log Message ${tc.kws[0].msgs[3]} ${EMPTY}
- Check Log Message ${tc.kws[0].msgs[4]} four
- Check Log Message ${tc.kws[0].msgs[5]} five
+ Check Log Message ${tc[0, 0]} one
+ Check Log Message ${tc[0, 1]} two
+ Check Log Message ${tc[0, 2]} three
+ Check Log Message ${tc[0, 3]} ${EMPTY}
+ Check Log Message ${tc[0, 4]} four
+ Check Log Message ${tc[0, 5]} five
Multiline arguments with user keyword
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} 1
- Check Log Message ${tc.kws[0].kws[0].msgs[1]} ${EMPTY}
- Check Log Message ${tc.kws[0].kws[0].msgs[2]} 2
- Check Log Message ${tc.kws[0].kws[0].msgs[3]} 3
- Check Log Message ${tc.kws[0].kws[0].msgs[4]} 4
- Check Log Message ${tc.kws[0].kws[0].msgs[5]} 5
+ Check Log Message ${tc[0, 0, 0]} 1
+ Check Log Message ${tc[0, 0, 1]} ${EMPTY}
+ Check Log Message ${tc[0, 0, 2]} 2
+ Check Log Message ${tc[0, 0, 3]} 3
+ Check Log Message ${tc[0, 0, 4]} 4
+ Check Log Message ${tc[0, 0, 5]} 5
Multiline assignment
Check Test Case ${TEST NAME}
@@ -48,18 +48,17 @@ Multiline in user keyword
Multiline test settings
${tc} = Check Test Case ${TEST NAME}
- @{expected} = Evaluate ['my'+str(i) for i in range(1,6)]
- Should Contain Tags ${tc} @{expected}
+ Should Have Tags ${tc} @{{[f'my{i}' for i in range(1,6)]}}
Should Be Equal ${tc.doc} One.\nTwo.\nThree.\n\n${SPACE*32}Second paragraph.
- Check Log Message ${tc.setup.msgs[0]} first
- Check Log Message ${tc.setup.msgs[1]} ${EMPTY}
- Check Log Message ${tc.setup.msgs[2]} last
+ Check Log Message ${tc.setup[0]} first
+ Check Log Message ${tc.setup[1]} ${EMPTY}
+ Check Log Message ${tc.setup[2]} last
Multiline user keyword settings and control structures
${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.kws[0]} Multiline user keyword settings and control structures
+ Check Keyword Data ${tc[0]} Multiline user keyword settings and control structures
... \${x} 1, 2 tags=keyword, tags
- Check Log Message ${tc.kws[0].teardown.msgs[0]} Bye!
+ Check Log Message ${tc[0].teardown[0]} Bye!
Multiline FOR Loop declaration
Check Test Case ${TEST NAME}
diff --git a/atest/robot/parsing/non_ascii_spaces.robot b/atest/robot/parsing/non_ascii_spaces.robot
index c077abe9767..3a7743ec85d 100644
--- a/atest/robot/parsing/non_ascii_spaces.robot
+++ b/atest/robot/parsing/non_ascii_spaces.robot
@@ -5,19 +5,19 @@ Resource atest_resource.robot
*** Test Cases ***
In suite settings
${tc} = Check Test Case In test and keywords
- Check Log Message ${tc.setup.kws[0].msgs[0]} ':\\xa0:'
- Check Log Message ${tc.setup.kws[1].msgs[0]} : :
- Check Log Message ${tc.teardown.kws[0].msgs[0]} ':\\u1680:'
- Check Log Message ${tc.teardown.kws[1].msgs[0]} : :
+ Check Log Message ${tc.setup[0, 0]} ':\\xa0:'
+ Check Log Message ${tc.setup[1, 0]} : :
+ Check Log Message ${tc.teardown[0, 0]} ':\\u1680:'
+ Check Log Message ${tc.teardown[1, 0]} : :
In test and keywords
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} ':\\xa0:'
- Check Log Message ${tc.kws[0].kws[1].msgs[0]} : :
- Check Log Message ${tc.kws[1].kws[0].msgs[0]} ':\\u1680:'
- Check Log Message ${tc.kws[1].kws[1].msgs[0]} : :
- Check Log Message ${tc.kws[2].kws[0].msgs[0]} ':\\u3000:'
- Check Log Message ${tc.kws[2].kws[1].msgs[0]} : :
+ Check Log Message ${tc[0, 0, 0]} ':\\xa0:'
+ Check Log Message ${tc[0, 1, 0]} : :
+ Check Log Message ${tc[1, 0, 0]} ':\\u1680:'
+ Check Log Message ${tc[1, 1, 0]} : :
+ Check Log Message ${tc[2, 0, 0]} ':\\u3000:'
+ Check Log Message ${tc[2, 1, 0]} : :
As separator
Check Test Case ${TESTNAME}
@@ -39,7 +39,10 @@ In FOR separator
In ELSE IF
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.body[0].body[3].body[0].msgs[0]} Should be executed
+ Check Log Message ${tc[0, 3, 0, 0]} Should be executed
In inline ELSE IF
Check Test Case ${TESTNAME}
+
+With embedded arguments and BDD prefixes
+ Check Test Case ${TESTNAME}
diff --git a/atest/robot/parsing/same_setting_multiple_times.robot b/atest/robot/parsing/same_setting_multiple_times.robot
index a6fbacd1043..82854d277c9 100644
--- a/atest/robot/parsing/same_setting_multiple_times.robot
+++ b/atest/robot/parsing/same_setting_multiple_times.robot
@@ -28,7 +28,7 @@ Test Teardown
Test Template
${tc} = Check Test Case Use Defaults
- Check Keyword Data ${tc.kws[0]} BuiltIn.Log Many args=Sleep, 0.1s
+ Check Keyword Data ${tc[0]} BuiltIn.Log Many args=Sleep, 0.1s
Test Timeout
${tc} = Check Test Case Use Defaults
@@ -36,9 +36,9 @@ Test Timeout
Test [Documentation]
${tc} = Check Test Case Test Settings
- Should Be Equal ${tc.kws[0].type} ERROR
- Should Be Equal ${tc.kws[0].status} FAIL
- Should Be Equal ${tc.kws[0].values[0]} [Documentation]
+ Should Be Equal ${tc[0].type} ERROR
+ Should Be Equal ${tc[0].status} FAIL
+ Should Be Equal ${tc[0].values[0]} [Documentation]
Test [Tags]
Check Test Tags Test Settings
@@ -53,7 +53,7 @@ Test [Teardown]
Test [Template]
${tc} = Check Test Case Test Settings
- Check Keyword Data ${tc.kws[7]} BuiltIn.Log args=No Operation
+ Check Keyword Data ${tc[7]} BuiltIn.Log args=No Operation
Test [Timeout]
${tc} = Check Test Case Test Settings
@@ -61,20 +61,20 @@ Test [Timeout]
Keyword [Arguments]
${tc} = Check Test Case Keyword Settings
- Check Keyword Data ${tc.kws[0]} Keyword Settings assign=\${ret} args=1, 2, 3 tags=K1 status=FAIL
- Check Log Message ${tc.kws[0].msgs[0]} Arguments: [ \${a1}='1' | \${a2}='2' | \${a3}='3' ] TRACE
+ Check Keyword Data ${tc[0]} Keyword Settings assign=\${ret} args=1, 2, 3 tags=K1 status=FAIL
+ Check Log Message ${tc[0, 0]} Arguments: [ \${a1}='1' | \${a2}='2' | \${a3}='3' ] TRACE
Keyword [Documentation]
${tc} = Check Test Case Keyword Settings
- Should Be Equal ${tc.kws[0].doc} ${EMPTY}
+ Should Be Equal ${tc[0].doc} ${EMPTY}
Keyword [Tags]
${tc} = Check Test Case Keyword Settings
- Should Be True list($tc.kws[0].tags) == ['K1']
+ Should Be True list($tc[0].tags) == ['K1']
Keyword [Timeout]
${tc} = Check Test Case Keyword Settings
- Should Be Equal ${tc.kws[0].timeout} ${NONE}
+ Should Be Equal ${tc[0].timeout} ${NONE}
Keyword [Return]
Check Test Case Keyword Settings
diff --git a/atest/robot/parsing/spaces_and_tabs.robot b/atest/robot/parsing/spaces_and_tabs.robot
index 1d514e1903a..f517f7239ae 100644
--- a/atest/robot/parsing/spaces_and_tabs.robot
+++ b/atest/robot/parsing/spaces_and_tabs.robot
@@ -14,16 +14,16 @@ Lot of spaces
Trailing spaces
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} No spaces at end
- Check Log Message ${tc.kws[1].msgs[0]} One space at end
- Check Log Message ${tc.kws[2].msgs[0]} Two spaces at end
- Check Log Message ${tc.kws[3].msgs[0]} Ten spaces at end
- Check Log Message ${tc.kws[4].msgs[0]} Tab at end
+ Check Log Message ${tc[0, 0]} No spaces at end
+ Check Log Message ${tc[1, 0]} One space at end
+ Check Log Message ${tc[2, 0]} Two spaces at end
+ Check Log Message ${tc[3, 0]} Ten spaces at end
+ Check Log Message ${tc[4, 0]} Tab at end
Tabs
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} I ignore tabs DEBUG
+ Check Log Message ${tc[0, 0]} I ignore tabs DEBUG
Tabs and spaces
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} I ignore tabs (and spaces) DEBUG
+ Check Log Message ${tc[0, 0]} I ignore tabs (and spaces) DEBUG
diff --git a/atest/robot/parsing/table_names.robot b/atest/robot/parsing/table_names.robot
index fcab2d919a1..21d8109333e 100644
--- a/atest/robot/parsing/table_names.robot
+++ b/atest/robot/parsing/table_names.robot
@@ -17,7 +17,7 @@ Test Cases section
Keywords section
${tc} = Check Test Case Test Case
- Check Log Message ${tc.kws[1].kws[0].kws[0].msgs[0]} "Keywords" was executed
+ Check Log Message ${tc[1, 0, 0, 0]} "Keywords" was executed
Comments section
Check Test Case Comment section exist
@@ -40,7 +40,7 @@ Invalid sections
[Setup] Run Tests ${EMPTY} parsing/invalid_table_names.robot
${tc} = Check Test Case Test in valid table
${path} = Normalize Path ${DATADIR}/parsing/invalid_tables_resource.robot
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} Keyword in valid table
+ Check Log Message ${tc[0, 0, 0]} Keyword in valid table
Length Should Be ${ERRORS} 4
Invalid Section Error 0 invalid_table_names.robot 1 *** Error ***
Invalid Section Error 1 invalid_table_names.robot 8 *** ***
@@ -51,7 +51,7 @@ Invalid sections
Check First Log Entry
[Arguments] ${test case name} ${expected}
${tc} = Check Test Case ${test case name}
- Check Log Message ${tc.kws[0].msgs[0]} ${expected}
+ Check Log Message ${tc[0, 0]} ${expected}
Invalid Section Error
[Arguments] ${index} ${file} ${lineno} ${header} ${test and task}=, 'Test Cases', 'Tasks'
diff --git a/atest/robot/parsing/test_case_settings.robot b/atest/robot/parsing/test_case_settings.robot
index 2991e73a3b8..b321b6df9ea 100644
--- a/atest/robot/parsing/test_case_settings.robot
+++ b/atest/robot/parsing/test_case_settings.robot
@@ -145,8 +145,8 @@ Setup and teardown with escaping
Template
[Documentation] Mainly tested elsewhere
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} Hello, world!
- Check Log Message ${tc.kws[1].msgs[0]} Hi, tellus!
+ Check Log Message ${tc[0, 0]} Hello, world!
+ Check Log Message ${tc[1, 0]} Hi, tellus!
Timeout
Verify Timeout 1 day
@@ -198,13 +198,13 @@ Verify Setup
[Arguments] ${message}
${tc} = Check Test Case ${TEST NAME}
Should Be Equal ${tc.setup.full_name} BuiltIn.Log
- Check Log Message ${tc.setup.msgs[0]} ${message}
+ Check Log Message ${tc.setup[0]} ${message}
Verify Teardown
[Arguments] ${message}
${tc} = Check Test Case ${TEST NAME}
Should Be Equal ${tc.teardown.full_name} BuiltIn.Log
- Check Log Message ${tc.teardown.msgs[0]} ${message}
+ Check Log Message ${tc.teardown[0]} ${message}
Verify Timeout
[Arguments] ${timeout}
diff --git a/atest/robot/parsing/translations.robot b/atest/robot/parsing/translations.robot
index dd171f36b9f..ebf6386d6e0 100644
--- a/atest/robot/parsing/translations.robot
+++ b/atest/robot/parsing/translations.robot
@@ -48,10 +48,12 @@ Per file configuration with multiple languages
Should Be Equal ${tc.doc} приклад
Invalid per file configuration
- Run Tests ${EMPTY} parsing/translations/per_file_config/many.robot
Error in file 0 parsing/translations/per_file_config/many.robot 4
... Invalid language configuration:
... Language 'invalid' not found nor importable as a language module.
+ Error in file 1 parsing/translations/per_file_config/many.robot 5
+ ... Invalid language configuration:
+ ... Language 'another invalid value' not found nor importable as a language module.
Per file configuration bleeds to other files
[Documentation] This is a technical limitation and will hopefully change!
@@ -74,20 +76,20 @@ Validate Translations
Should Be Equal ${tc.timeout} 1 minute
Should Be Equal ${tc.setup.full_name} Test Setup
Should Be Equal ${tc.teardown.full_name} Test Teardown
- Should Be Equal ${tc.body[0].full_name} Test Template
- Should Be Equal ${tc.body[0].tags} ${{['keyword', 'tags']}}
+ Should Be Equal ${tc[0].full_name} Test Template
+ Should Be Equal ${tc[0].tags} ${{['keyword', 'tags']}}
${tc} = Check Test Case Test with settings
Should Be Equal ${tc.doc} Test documentation.
Should Be Equal ${tc.tags} ${{['test', 'tags', 'own tag']}}
Should Be Equal ${tc.timeout} ${NONE}
Should Be Equal ${tc.setup.full_name} ${NONE}
Should Be Equal ${tc.teardown.full_name} ${NONE}
- Should Be Equal ${tc.body[0].full_name} Keyword
- Should Be Equal ${tc.body[0].doc} Keyword documentation.
- Should Be Equal ${tc.body[0].tags} ${{['keyword', 'tags', 'own tag']}}
- Should Be Equal ${tc.body[0].timeout} 1 hour
- Should Be Equal ${tc.body[0].setup.full_name} BuiltIn.Log
- Should Be Equal ${tc.body[0].teardown.full_name} BuiltIn.No Operation
+ Should Be Equal ${tc[0].full_name} Keyword
+ Should Be Equal ${tc[0].doc} Keyword documentation.
+ Should Be Equal ${tc[0].tags} ${{['keyword', 'tags', 'own tag']}}
+ Should Be Equal ${tc[0].timeout} 1 hour
+ Should Be Equal ${tc[0].setup.full_name} BuiltIn.Log
+ Should Be Equal ${tc[0].teardown.full_name} BuiltIn.No Operation
Validate Task Translations
${tc} = Check Test Case Task without settings
@@ -96,11 +98,11 @@ Validate Task Translations
Should Be Equal ${tc.timeout} 1 minute
Should Be Equal ${tc.setup.full_name} Task Setup
Should Be Equal ${tc.teardown.full_name} Task Teardown
- Should Be Equal ${tc.body[0].full_name} Task Template
+ Should Be Equal ${tc[0].full_name} Task Template
${tc} = Check Test Case Task with settings
Should Be Equal ${tc.doc} Task documentation.
Should Be Equal ${tc.tags} ${{['task', 'tags', 'own tag']}}
Should Be Equal ${tc.timeout} ${NONE}
Should Be Equal ${tc.setup.full_name} ${NONE}
Should Be Equal ${tc.teardown.full_name} ${NONE}
- Should Be Equal ${tc.body[0].full_name} BuiltIn.Log
+ Should Be Equal ${tc[0].full_name} BuiltIn.Log
diff --git a/atest/robot/parsing/user_keyword_settings.robot b/atest/robot/parsing/user_keyword_settings.robot
index 5fb87406836..8ab9eaec0ea 100644
--- a/atest/robot/parsing/user_keyword_settings.robot
+++ b/atest/robot/parsing/user_keyword_settings.robot
@@ -5,11 +5,11 @@ Resource atest_resource.robot
*** Test Cases ***
Name
${tc} = Check Test Case Normal name
- Should Be Equal ${tc.kws[0].full_name} Normal name
+ Should Be Equal ${tc[0].full_name} Normal name
Names are not formatted
${tc} = Check Test Case Names are not formatted
- FOR ${kw} IN @{tc.kws}
+ FOR ${kw} IN @{tc.body}
Should Be Equal ${kw.full_name} user_keyword nameS _are_not_ FORmatted
END
@@ -43,19 +43,19 @@ Documentation with escaping
Arguments
[Documentation] Tested more thoroughly elsewhere.
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} mandatory
- Check Log Message ${tc.kws[0].kws[0].msgs[1]} default
- Should Be True ${tc.kws[0].args} == ('mandatory',)
- Check Log Message ${tc.kws[1].kws[0].msgs[0]} 1
- Check Log Message ${tc.kws[1].kws[0].msgs[1]} 2
- Should Be True ${tc.kws[1].args} == ('1', '2')
- Check Log Message ${tc.kws[2].kws[0].msgs[0]} 1
- Check Log Message ${tc.kws[2].kws[0].msgs[1]} 2
- Check Log Message ${tc.kws[2].kws[0].msgs[2]} 3
- Check Log Message ${tc.kws[2].kws[0].msgs[3]} 4
- Check Log Message ${tc.kws[2].kws[0].msgs[4]} 5
- Check Log Message ${tc.kws[2].kws[0].msgs[5]} key=6
- Should Be True ${tc.kws[2].args} == ('\${1}', '\${2}', '\${3}', '\${4}', '\${5}', 'key=\${6}')
+ Check Log Message ${tc[0, 0, 0]} mandatory
+ Check Log Message ${tc[0, 0, 1]} default
+ Should Be True ${tc[0].args} == ('mandatory',)
+ Check Log Message ${tc[1, 0, 0]} 1
+ Check Log Message ${tc[1, 0, 1]} 2
+ Should Be True ${tc[1].args} == ('1', '2')
+ Check Log Message ${tc[2, 0, 0]} 1
+ Check Log Message ${tc[2, 0, 1]} 2
+ Check Log Message ${tc[2, 0, 2]} 3
+ Check Log Message ${tc[2, 0, 3]} 4
+ Check Log Message ${tc[2, 0, 4]} 5
+ Check Log Message ${tc[2, 0, 5]} key=6
+ Should Be True ${tc[2].args} == ('\${1}', '\${2}', '\${3}', '\${4}', '\${5}', 'key=\${6}')
Teardown
Verify Teardown Keyword teardown
@@ -69,29 +69,29 @@ Teardown with escaping
Return
[Documentation] [Return] is deprecated. In parsing it is transformed to RETURN.
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.body[0].body[0].type} RETURN
- Should Be Equal ${tc.body[0].body[0].values} ${{('Return value',)}}
+ Should Be Equal ${tc[0, 0].type} RETURN
+ Should Be Equal ${tc[0, 0].values} ${{('Return value',)}}
Error in File 0 parsing/user_keyword_settings.robot 167
... The '[[]Return]' setting is deprecated. Use the 'RETURN' statement instead. level=WARN
Return using variables
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.body[0].body[1].type} RETURN
- Should Be Equal ${tc.body[0].body[1].values} ${{('\${ret}',)}}
+ Should Be Equal ${tc[0, 1].type} RETURN
+ Should Be Equal ${tc[0, 1].values} ${{('\${ret}',)}}
Error in File 1 parsing/user_keyword_settings.robot 171
... The '[[]Return]' setting is deprecated. Use the 'RETURN' statement instead. level=WARN
Return multiple
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.body[0].body[1].type} RETURN
- Should Be Equal ${tc.body[0].body[1].values} ${{('\${arg1}', '+', '\${arg2}', '=', '\${result}')}}
+ Should Be Equal ${tc[0, 1].type} RETURN
+ Should Be Equal ${tc[0, 1].values} ${{('\${arg1}', '+', '\${arg2}', '=', '\${result}')}}
Error in File 2 parsing/user_keyword_settings.robot 176
... The '[[]Return]' setting is deprecated. Use the 'RETURN' statement instead. level=WARN
Return with escaping
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.body[0].body[0].type} RETURN
- Should Be Equal ${tc.body[0].body[0].values} ${{('\\\${XXX}', 'c:\\\\temp', '\\', '\\\\')}}
+ Should Be Equal ${tc[0, 0].type} RETURN
+ Should Be Equal ${tc[0, 0].values} ${{('\\\${XXX}', 'c:\\\\temp', '\\', '\\\\')}}
Error in File 3 parsing/user_keyword_settings.robot 179
... The '[[]Return]' setting is deprecated. Use the 'RETURN' statement instead. level=WARN
@@ -106,8 +106,8 @@ Invalid timeout
Multiple settings
Verify Documentation Documentation for a user keyword
- Verify Teardown Teardown World
- Verify Timeout 6 minutes
+ Verify Teardown Teardown World
+ Verify Timeout 6 minutes
Invalid setting
Check Test Case ${TEST NAME}
@@ -127,15 +127,15 @@ Invalid empty line continuation in arguments should throw an error
Verify Documentation
[Arguments] ${doc} ${test}=${TEST NAME}
${tc} = Check Test Case ${test}
- Should Be Equal ${tc.kws[0].doc} ${doc}
+ Should Be Equal ${tc[0].doc} ${doc}
Verify Teardown
[Arguments] ${message}
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.kws[0].teardown.full_name} BuiltIn.Log
- Check Log Message ${tc.kws[0].teardown.msgs[0]} ${message}
+ Should Be Equal ${tc[0].teardown.full_name} BuiltIn.Log
+ Check Log Message ${tc[0].teardown[0]} ${message}
Verify Timeout
[Arguments] ${timeout}
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.kws[0].timeout} ${timeout}
+ Should Be Equal ${tc[0].timeout} ${timeout}
diff --git a/atest/robot/parsing/utf8_data/utf8_in_tsv.robot b/atest/robot/parsing/utf8_data/utf8_in_tsv.robot
index 5cf83c7bd4a..8b63e3cd8cf 100644
--- a/atest/robot/parsing/utf8_data/utf8_in_tsv.robot
+++ b/atest/robot/parsing/utf8_data/utf8_in_tsv.robot
@@ -1,25 +1,25 @@
*** Settings ***
-Suite Setup Run Tests ${EMPTY} parsing/utf8_data.tsv
-Resource atest_resource.robot
+Suite Setup Run Tests ${EMPTY} parsing/utf8_data.tsv
+Resource atest_resource.robot
*** Test Cases ***
UTF-8 In Metadata
- Should Be Equal ${SUITE.doc} Testing that reading and writing of Unicode (äöå §½€ etc.) works properly.
- Should Be Equal as Strings ${SUITE.metadata} {Ä: §}
- Check Test Tags UTF-8 tag-§ tag-€
- Check Test Doc UTF-8 äöå §½€
+ Should Be Equal ${SUITE.doc} Testing that reading and writing of Unicode (äöå §½€ etc.) works properly.
+ Should Be Equal As Strings ${SUITE.metadata} {Ä: §}
+ Check Test Tags UTF-8 tag-§ tag-€
+ Check Test Doc UTF-8 äöå §½€
UTF-8 In Keyword Arguments
- ${tc} = Check Test Case UTF-8
- Check Log Message ${tc.setup.msgs[0]} äöå
- Check Log Message ${tc.kws[0].msgs[0]} §½€
- Check Log Message ${tc.kws[1].msgs[0]} äöå §½€
- Check Log Message ${tc.kws[2].kws[0].msgs[0]} äöå
- Check Log Message ${tc.kws[2].kws[1].msgs[0]} äöå §½€
- Check Log Message ${tc.kws[2].kws[2].msgs[0]} §½€
+ ${tc} = Check Test Case UTF-8
+ Check Log Message ${tc.setup[0]} äöå
+ Check Log Message ${tc[0, 0]} §½€
+ Check Log Message ${tc[1, 0]} äöå §½€
+ Check Log Message ${tc[2, 0, 0]} äöå
+ Check Log Message ${tc[2, 1, 0]} äöå §½€
+ Check Log Message ${tc[2, 2, 0]} §½€
UTF-8 In Test Case And UK Names
- ${tc} = Check Test Case UTF-8 Name Äöå §½€"
- Check Keyword Data ${tc.kws[0]} Äöå §½€ \${ret}
- Check Log Message ${tc.kws[1].msgs[0]} äöå §½€
- Check Log Message ${tc.kws[3].msgs[0]} value
+ ${tc} = Check Test Case UTF-8 Name Äöå §½€"
+ Check Keyword Data ${tc[0]} Äöå §½€ \${ret}
+ Check Log Message ${tc[1, 0]} äöå §½€
+ Check Log Message ${tc[3, 0]} value
diff --git a/atest/robot/parsing/utf8_data/utf8_in_txt.robot b/atest/robot/parsing/utf8_data/utf8_in_txt.robot
index fca9e1856d3..c22049aa238 100644
--- a/atest/robot/parsing/utf8_data/utf8_in_txt.robot
+++ b/atest/robot/parsing/utf8_data/utf8_in_txt.robot
@@ -1,25 +1,25 @@
*** Settings ***
-Suite Setup Run Tests ${EMPTY} parsing/utf8_data.robot
-Resource atest_resource.robot
+Suite Setup Run Tests ${EMPTY} parsing/utf8_data.robot
+Resource atest_resource.robot
*** Test Cases ***
UTF-8 In Metadata
- Should Be Equal ${SUITE.doc} Testing that reading and writing of Unicode (äöå §½€ etc.) works properly.
- Should Be Equal as Strings ${SUITE.metadata} {Ä: §}
- Check Test Tags UTF-8 tag-§ tag-€
- Check Test Doc UTF-8 äöå §½€
+ Should Be Equal ${SUITE.doc} Testing that reading and writing of Unicode (äöå §½€ etc.) works properly.
+ Should Be Equal As Strings ${SUITE.metadata} {Ä: §}
+ Check Test Tags UTF-8 tag-§ tag-€
+ Check Test Doc UTF-8 äöå §½€
UTF-8 In Keyword Arguments
- ${tc} = Check Test Case UTF-8
- Check Log Message ${tc.setup.msgs[0]} äöå
- Check Log Message ${tc.kws[0].msgs[0]} §½€
- Check Log Message ${tc.kws[1].msgs[0]} äöå §½€
- Check Log Message ${tc.kws[2].kws[0].msgs[0]} äöå
- Check Log Message ${tc.kws[2].kws[1].msgs[0]} äöå §½€
- Check Log Message ${tc.kws[2].kws[2].msgs[0]} §½€
+ ${tc} = Check Test Case UTF-8
+ Check Log Message ${tc.setup[0]} äöå
+ Check Log Message ${tc[0, 0]} §½€
+ Check Log Message ${tc[1, 0]} äöå §½€
+ Check Log Message ${tc[2, 0, 0]} äöå
+ Check Log Message ${tc[2, 1, 0]} äöå §½€
+ Check Log Message ${tc[2, 2, 0]} §½€
UTF-8 In Test Case And UK Names
- ${tc} = Check Test Case UTF-8 Name Äöå §½€"
- Check Keyword Data ${tc.kws[0]} Äöå §½€ \${ret}
- Check Log Message ${tc.kws[1].msgs[0]} äöå §½€
- Check Log Message ${tc.kws[3].msgs[0]} value
+ ${tc} = Check Test Case UTF-8 Name Äöå §½€"
+ Check Keyword Data ${tc[0]} Äöå §½€ \${ret}
+ Check Log Message ${tc[1, 0]} äöå §½€
+ Check Log Message ${tc[3, 0]} value
diff --git a/atest/robot/rebot/compatibility.robot b/atest/robot/rebot/compatibility.robot
index 1957f5f8f56..4df330d0753 100644
--- a/atest/robot/rebot/compatibility.robot
+++ b/atest/robot/rebot/compatibility.robot
@@ -21,9 +21,9 @@ Suite only
Message directly under test
Run Rebot And Validate Statistics rebot/issue-3762.xml 1 0
${tc} = Check Test Case test A
- Check Log Message ${tc.body[0]} Hi from test WARN
- Check Log Message ${tc.body[1].body[0]} Hi from keyword WARN
- Check Log Message ${tc.body[2]} Hi from test again INFO
+ Check Log Message ${tc[0]} Hi from test WARN
+ Check Log Message ${tc[1, 0]} Hi from keyword WARN
+ Check Log Message ${tc[2]} Hi from test again INFO
*** Keywords ***
Run Rebot And Validate Statistics
diff --git a/atest/robot/rebot/filter_by_names.robot b/atest/robot/rebot/filter_by_names.robot
index 2293f80bbec..971b8a36e7a 100644
--- a/atest/robot/rebot/filter_by_names.robot
+++ b/atest/robot/rebot/filter_by_names.robot
@@ -22,16 +22,10 @@ ${INPUT FILE} %{TEMPDIR}${/}robot-test-file.xml
Run And Check Tests --test *one --test Fi?st First Second One Third One
Run And Check Tests --test [Great]Lob[sterB]estCase[!3-9] GlobTestCase1 GlobTestCase2
---test is cumulative with --include
- Run And Check Tests --test fifth --include t2 First Fifth Suite1 Second SubSuite3 Second
-
---exclude wins ovet --test
- Run And Check Tests --test fi* --exclude t1 Fifth
-
--test not matching
Failing Rebot
... Suite 'Root' contains no tests matching name 'nonex'.
- ... --test nonex ${INPUT FILE}
+ ... --test nonex
--test not matching with multiple inputs
Failing Rebot
@@ -41,6 +35,18 @@ ${INPUT FILE} %{TEMPDIR}${/}robot-test-file.xml
... Suite 'My Name' contains no tests matching name 'nonex'.
... --test nonex -N "My Name" ${INPUT FILE} ${INPUT FILE}
+--test and --include must both match
+ Run And Check Tests --test first --include t1 -i f1 First
+ Failing Rebot
+ ... Suite 'Root' contains no tests matching name 'fifth' and matching tag 't1'.
+ ... --test fifth --include t1
+
+--exclude wins over --test
+ Run And Check Tests --test fi* --exclude t1 Fifth
+ Failing Rebot
+ ... Suite 'Root' contains no tests matching name 'first' and not matching tag 'f1'.
+ ... --test first --exclude f1
+
--suite once
Run And Check Suites --suite tsuite1 Tsuite1
@@ -96,7 +102,7 @@ ${INPUT FILE} %{TEMPDIR}${/}robot-test-file.xml
Should Contain Tests ${SUITE} Suite1 First Suite3 First
--suite, --test, --include and --exclude
- Run Suites --suite sub* --suite "custom name *" --test *first -s nomatch -t nomatch --include sub3 --exclude t1
+ Run Suites --suite sub* --suite "custom name *" --test "subsuite3 second" -t *first -s nomatch -t nomatch --include f1 --exclude t1
Should Contain Suites ${SUITE} Suites
Should Contain Suites ${SUITE.suites[0]} Custom name for 📂 'subsuites2' Subsuites
Should Contain Tests ${SUITE} SubSuite2 First SubSuite3 Second
@@ -158,6 +164,6 @@ Run Suites
Stderr Should Be Empty
Failing Rebot
- [Arguments] ${error} ${options} ${sources}
+ [Arguments] ${error} ${options} ${sources}=${INPUT FILE}
Run Rebot Without Processing Output ${options} ${sources}
Stderr Should Be Equal To [ ERROR ] ${error}${USAGE TIP}\n
diff --git a/atest/robot/rebot/json_output_and_input.robot b/atest/robot/rebot/json_output_and_input.robot
index 56befc7dd2c..8fc26e2124f 100644
--- a/atest/robot/rebot/json_output_and_input.robot
+++ b/atest/robot/rebot/json_output_and_input.robot
@@ -7,20 +7,40 @@ ${XML} %{TEMPDIR}/rebot.xml
${JSON} %{TEMPDIR}/rebot.json
*** Test Cases ***
-JSON output
- Outputs should be equal ${JSON} ${XML}
+JSON output contains same suite information as XML output
+ Outputs Should Contain Same Data ${JSON} ${XML}
+
+JSON output structure
+ [Documentation] JSON schema validation would be good, but it's too slow with big output files.
+ ... The test after this one validates a smaller suite against a schema.
+ ${data} = Evaluate json.load(open($JSON, encoding='UTF-8'))
+ Lists Should Be Equal ${data} ${{['generator', 'generated', 'rpa', 'suite', 'statistics', 'errors']}}
+ Should Match ${data}[generator] Rebot ?.* (* on *)
+ Should Match ${data}[generated] 20??-??-??T??:??:??.??????
+ Should Be Equal ${data}[rpa] ${False}
+ Should Be Equal ${data}[suite][name] Misc
+ Should Be Equal ${data}[suite][suites][1][name] Everything
+ Should Be Equal ${data}[statistics][total][skip] ${3}
+ Should Be Equal ${data}[statistics][tags][4][label] f1
+ Should Be Equal ${data}[statistics][suites][-1][id] s1-s17
+ Should Be Equal ${data}[errors][0][level] ERROR
+
+JSON output schema validation
+ [Tags] require-jsonschema
+ Run Rebot Without Processing Output --suite Everything --output %{TEMPDIR}/everything.json ${JSON}
+ Validate JSON Output %{TEMPDIR}/everything.json
JSON input
Run Rebot ${EMPTY} ${JSON}
- Outputs should be equal ${JSON} ${OUTFILE}
+ Outputs Should Contain Same Data ${JSON} ${OUTFILE}
JSON input combined
Run Rebot ${EMPTY} ${XML} ${XML}
Copy Previous Outfile # Expected result
Run Rebot ${EMPTY} ${JSON} ${XML}
- Outputs should be equal ${OUTFILE} ${OUTFILE COPY}
+ Outputs Should Contain Same Data ${OUTFILE} ${OUTFILE COPY}
Run Rebot ${EMPTY} ${JSON} ${JSON}
- Outputs should be equal ${OUTFILE} ${OUTFILE COPY}
+ Outputs Should Contain Same Data ${OUTFILE} ${OUTFILE COPY}
Invalid JSON input
Create File ${JSON} bad
@@ -32,6 +52,14 @@ Invalid JSON input
... Invalid JSON data: *
Stderr Should Match [[] ERROR ] ${error}${USAGE TIP}\n
+Non-existing JSON input
+ Run Rebot Without Processing Output ${EMPTY} non_existing.json
+ ${json} = Normalize Path ${DATADIR}/non_existing.json
+ VAR ${error}
+ ... Reading JSON source '${json}' failed:
+ ... No such file or directory
+ Stderr Should Match [[] ERROR ] ${error}${USAGE TIP}\n
+
*** Keywords ***
Create XML and JSON outputs
Create Output With Robot ${XML} ${EMPTY} misc
diff --git a/atest/robot/rebot/merge.robot b/atest/robot/rebot/merge.robot
index 0d7cfc1a8e6..b2539d6214a 100644
--- a/atest/robot/rebot/merge.robot
+++ b/atest/robot/rebot/merge.robot
@@ -37,6 +37,10 @@ Merge suite documentation and metadata
[Setup] Should Be Equal ${PREV_TEST_STATUS} PASS
Suite documentation and metadata should have been merged
+Suite elapsed time should be updated
+ [Setup] Should Be Equal ${PREV_TEST_STATUS} PASS
+ Should Be True $SUITE.elapsed_time > $ORIGINAL_ELAPSED
+
Merge re-executed and re-re-executed tests
Re-run tests
Re-re-run tests
@@ -82,8 +86,8 @@ Merge ignores skip
... *HTML* Test has been re-executed and results merged.
... Latter result had SKIP status and was ignored. Message:
Should Contain Tests ${SUITE}
- ... Pass=PASS:${prefix}\nTest skipped using '--skip' command line option.
- ... Fail=FAIL:${prefix}\nTest skipped using '--skip' command line option.Original message:\nNot <b>HTML</b> fail
+ ... Pass=PASS:${prefix}\nTest skipped using 'NOT skip' tag pattern.
+ ... Fail=FAIL:${prefix}\nTest skipped using 'NOT skip' tag pattern.Original message:\nNot <b>HTML</b> fail
... Skip=SKIP:${prefix}\nHTML skipOriginal message:\nHTML skip
*** Keywords ***
@@ -95,6 +99,7 @@ Run original tests
... --metadata Original:True
Create Output With Robot ${ORIGINAL} ${options} ${SUITES}
Verify original tests
+ VAR ${ORIGINAL ELAPSED} ${SUITE.elapsed_time} scope=SUITE
Verify original tests
Should Be Equal ${SUITE.name} Suites
@@ -115,6 +120,7 @@ Re-run tests
... --variable TEARDOWN_MSG:New! # -- ;; --
... --variable SETUP:NONE # Affects misc/suites/subsuites/sub1.robot
... --variable TEARDOWN:NONE # -- ;; --
+ ... --variable SLEEP:0.5 # -- ;; --
... --rerunfailed ${ORIGINAL} ${options}
Create Output With Robot ${MERGE 1} ${options} ${SUITES}
Should Be Equal ${SUITE.name} Suites
@@ -178,8 +184,8 @@ Suite setup and teardown should have been merged
Should Be Equal ${SUITE.setup.full_name} BuiltIn.No Operation
Should Be Equal ${SUITE.teardown.name} ${NONE}
Should Be Equal ${SUITE.suites[1].name} Fourth
- Check Log Message ${SUITE.suites[1].setup.msgs[0]} Rerun!
- Check Log Message ${SUITE.suites[1].teardown.msgs[0]} New!
+ Check Log Message ${SUITE.suites[1].setup[0]} Rerun!
+ Check Log Message ${SUITE.suites[1].teardown[0]} New!
Should Be Equal ${SUITE.suites[2].suites[0].name} Sub1
Should Be Equal ${SUITE.suites[2].suites[0].setup.name} ${NONE}
Should Be Equal ${SUITE.suites[2].suites[0].teardown.name} ${NONE}
@@ -243,7 +249,7 @@ Warnings should have been merged
Check Log Message ${ERRORS[0]} Original message WARN
Check Log Message ${ERRORS[1]} Override WARN
${tc} = Check Test Case SubSuite1 First
- Check Log Message ${tc.kws[0].msgs[0]} Override WARN
+ Check Log Message ${tc[0, 0]} Override WARN
Merge should have failed
Stderr Should Be Equal To
diff --git a/atest/robot/rpa/run_rpa_tasks.robot b/atest/robot/rpa/run_rpa_tasks.robot
index 33de99babfa..b2d9b8a762e 100644
--- a/atest/robot/rpa/run_rpa_tasks.robot
+++ b/atest/robot/rpa/run_rpa_tasks.robot
@@ -39,7 +39,7 @@ Conflicting headers with --rpa are fine
Conflicting headers with --norpa are fine
[Template] Run and validate test cases
- --NorPA -v TIMEOUT:Test rpa/ @{ALL TASKS}
+ --NorPA -v TIMEOUT:Test -v RPA:False rpa/ @{ALL TASKS}
Conflicting headers in same file cause error
[Documentation] Using --rpa or --norpa doesn't affect the behavior.
diff --git a/atest/robot/rpa/task_aliases.robot b/atest/robot/rpa/task_aliases.robot
index 4a62e6aad09..533eab1baa1 100644
--- a/atest/robot/rpa/task_aliases.robot
+++ b/atest/robot/rpa/task_aliases.robot
@@ -4,25 +4,25 @@ Resource atest_resource.robot
*** Test Cases ***
Defaults
- ${tc} = Check Test Tags ${TESTNAME} task tags
- Check timeout message ${tc.setup.msgs[0]} 1 minute 10 seconds
- Check log message ${tc.setup.msgs[1]} Setup has an alias!
- Check timeout message ${tc.kws[0].msgs[0]} 1 minute 10 seconds
- Check log message ${tc.kws[0].msgs[1]} Using default settings
- Check log message ${tc.teardown.msgs[0]} Also teardown has an alias!!
- Should be equal ${tc.timeout} 1 minute 10 seconds
+ ${tc} = Check Test Tags ${TESTNAME} task tags
+ Check timeout message ${tc.setup[0]} 1 minute 10 seconds
+ Check log message ${tc.setup[1]} Setup has an alias!
+ Check timeout message ${tc[0, 0]} 1 minute 10 seconds
+ Check log message ${tc[0, 1]} Using default settings
+ Check log message ${tc.teardown[0]} Also teardown has an alias!!
+ Should be equal ${tc.timeout} 1 minute 10 seconds
Override
- ${tc} = Check Test Tags ${TESTNAME} task tags own
- Check log message ${tc.setup.msgs[0]} Overriding setup
- Check log message ${tc.kws[0].msgs[0]} Overriding settings
- Check log message ${tc.teardown.msgs[0]} Overriding teardown as well
- Should be equal ${tc.timeout} ${NONE}
+ ${tc} = Check Test Tags ${TESTNAME} task tags own
+ Check log message ${tc.setup[0]} Overriding setup
+ Check log message ${tc[0, 0]} Overriding settings
+ Check log message ${tc.teardown[0]} Overriding teardown as well
+ Should be equal ${tc.timeout} ${NONE}
Task timeout exceeded
${tc} = Check Test Case ${TESTNAME}
- Check timeout message ${tc.kws[0].msgs[0]} 99 milliseconds
- Check log message ${tc.kws[0].msgs[1]} Task timeout 99 milliseconds exceeded. FAIL
+ Check timeout message ${tc[0, 0]} 99 milliseconds
+ Check log message ${tc[0, 1]} Task timeout 99 milliseconds exceeded. FAIL
Invalid task timeout
Check Test Case ${TESTNAME}
@@ -44,16 +44,16 @@ Task settings are not allowed in resource file
In init file
Run Tests --loglevel DEBUG rpa/tasks
- ${tc} = Check Test Tags Defaults file tag task tags
- Check timeout message ${tc.setup.msgs[0]} 1 minute 10 seconds
- Check log message ${tc.setup.msgs[1]} Setup has an alias!
- Check timeout message ${tc.body[0].msgs[0]} 1 minute 10 seconds
- Check log message ${tc.teardown.msgs[0]} Also teardown has an alias!!
- Should be equal ${tc.timeout} 1 minute 10 seconds
- ${tc} = Check Test Tags Override file tag task tags own
- Check log message ${tc.setup.msgs[0]} Overriding setup
- Check log message ${tc.teardown.msgs[0]} Overriding teardown as well
- Should be equal ${tc.timeout} ${NONE}
+ ${tc} = Check Test Tags Defaults file tag task tags
+ Check timeout message ${tc.setup[0]} 1 minute 10 seconds
+ Check log message ${tc.setup[1]} Setup has an alias!
+ Check timeout message ${tc[0, 0]} 1 minute 10 seconds
+ Check log message ${tc.teardown[0]} Also teardown has an alias!!
+ Should be equal ${tc.timeout} 1 minute 10 seconds
+ ${tc} = Check Test Tags Override file tag task tags own
+ Check log message ${tc.setup[0]} Overriding setup
+ Check log message ${tc.teardown[0]} Overriding teardown as well
+ Should be equal ${tc.timeout} ${NONE}
Should be empty ${ERRORS}
*** Keywords ***
diff --git a/atest/robot/running/GetNestingLevel.py b/atest/robot/running/GetNestingLevel.py
new file mode 100644
index 00000000000..1f091c2a720
--- /dev/null
+++ b/atest/robot/running/GetNestingLevel.py
@@ -0,0 +1,21 @@
+from robot.api import SuiteVisitor
+
+
+class Nesting(SuiteVisitor):
+
+ def __init__(self):
+ self.level = 0
+ self.max = 0
+
+ def start_keyword(self, kw):
+ self.level += 1
+ self.max = max(self.level, self.max)
+
+ def end_keyword(self, kw):
+ self.level -= 1
+
+
+def get_nesting_level(test):
+ nesting = Nesting()
+ test.visit(nesting)
+ return nesting.max
diff --git a/atest/robot/running/continue_on_failure.robot b/atest/robot/running/continue_on_failure.robot
index bcffd3559c4..784d162727f 100644
--- a/atest/robot/running/continue_on_failure.robot
+++ b/atest/robot/running/continue_on_failure.robot
@@ -6,93 +6,93 @@ Resource atest_resource.robot
Continue in test
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[1].msgs[0]} This should be executed
+ Check Log Message ${tc[1, 0]} This should be executed
Continue in user keyword
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[1].msgs[0]} This should be executed in Test Case
+ Check Log Message ${tc[0, 1, 0]} This should be executed in Test Case
Continue in test with several continuable failures
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[1].msgs[0]} This should be executed
- Check Log Message ${tc.kws[3].msgs[0]} This should also be executed
- Check Log Message ${tc.kws[5].msgs[0]} This too should also be executed
+ Check Log Message ${tc[1, 0]} This should be executed
+ Check Log Message ${tc[3, 0]} This should also be executed
+ Check Log Message ${tc[5, 0]} This too should also be executed
Continue in user keyword with several continuable failures
${tc}= Check Test Case ${TESTNAME}
- Verify all failures in user keyword ${tc.kws[0]} Test Case
- Verify all failures in user keyword ${tc.kws[1]} Test Case, Again
+ Verify all failures in user keyword ${tc[0]} Test Case
+ Verify all failures in user keyword ${tc[1]} Test Case, Again
Continuable and regular failure
${tc}= Check Test Case ${TESTNAME}
- Length Should Be ${tc.kws} 4
- Should Be Equal ${tc.kws[-1].status} NOT RUN
+ Length Should Be ${tc.body} 4
+ Should Be Equal ${tc[-1].status} NOT RUN
Continue in nested user keyword
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[1].msgs[0]} This should be executed in Top Level UK (with ∏ön ÄßÇïï €§)
- Verify all failures in user keyword ${tc.kws[0].kws[2]} Nested UK
+ Check Log Message ${tc[0, 1, 0]} This should be executed in Top Level UK (with ∏ön ÄßÇïï €§)
+ Verify all failures in user keyword ${tc[0, 2]} Nested UK
Continuable and regular failure in UK
Check Test Case ${TESTNAME}
Several continuable failures and regular failure in nested UK
${tc}= Check Test Case ${TESTNAME}
- Verify all failures in user keyword ${tc.kws[0].kws[2]} Nested UK
- Verify all failures in user keyword ${tc.kws[1].kws[1].kws[2]} Nested UK
+ Verify all failures in user keyword ${tc[0, 2]} Nested UK
+ Verify all failures in user keyword ${tc[1, 1, 2]} Nested UK
Continue when setting variables
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} \${ret} = None
- Check Log Message ${tc.kws[0].msgs[1]} ContinuableApocalypseException: Can be continued FAIL
- Check Log Message ${tc.kws[2].msgs[0]} \${r1} = None
- Check Log Message ${tc.kws[2].msgs[1]} \${r2} = None
- Check Log Message ${tc.kws[2].msgs[2]} \${r3} = None
- Check Log Message ${tc.kws[2].msgs[3]} ContinuableApocalypseException: Can be continued FAIL
- Check Log Message ${tc.kws[4].msgs[0]} \@{list} = [ ]
- Check Log Message ${tc.kws[4].msgs[1]} ContinuableApocalypseException: Can be continued FAIL
- Check Log Message ${tc.kws[6].msgs[0]} No jokes FAIL
- Length Should Be ${tc.kws[6].msgs} 1
+ Check Log Message ${tc[0, 0]} \${ret} = None
+ Check Log Message ${tc[0, 1]} ContinuableApocalypseException: Can be continued FAIL
+ Check Log Message ${tc[2, 0]} \${r1} = None
+ Check Log Message ${tc[2, 1]} \${r2} = None
+ Check Log Message ${tc[2, 2]} \${r3} = None
+ Check Log Message ${tc[2, 3]} ContinuableApocalypseException: Can be continued FAIL
+ Check Log Message ${tc[4, 0]} \@{list} = [ ]
+ Check Log Message ${tc[4, 1]} ContinuableApocalypseException: Can be continued FAIL
+ Check Log Message ${tc[6, 0]} No jokes FAIL
+ Length Should Be ${tc[6].body} 1
Continuable failure in user keyword returning value
Check Test Case ${TESTNAME}
Continue in test setup
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.setup.kws[1].msgs[0]} This should be executed in Test Setup
- Should Be Empty ${tc.kws}
+ Check Log Message ${tc.setup[1, 0]} This should be executed in Test Setup
+ Should Be Empty ${tc.body}
Continue in test teardown
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.teardown.kws[1].msgs[0]} This should be executed in Test Teardown
+ Check Log Message ${tc.teardown[1, 0]} This should be executed in Test Teardown
Continue many times in test setup and teardown
${tc}= Check Test Case ${TESTNAME}
- Verify all failures in user keyword ${tc.setup} Test Setup
- Should Be Empty ${tc.kws}
+ Verify all failures in user keyword ${tc.setup} Test Setup
+ Should Be Empty ${tc.body}
Verify all failures in user keyword ${tc.teardown} Test Teardown
Continue in suite teardown
${suite}= Get Test Suite Continue On Failure
- Check Log Message ${suite.teardown.kws[1].msgs[0]} This should be executed in Suite Teardown
+ Check Log Message ${suite.teardown[1, 0]} This should be executed in Suite Teardown
Continue in suite setup
${suite}= Get Test Suite Continue On Failure In Suite Setup
- Check Log Message ${suite.setup.kws[1].msgs[0]} This should be executed in Suite Setup (with ∏ön ÄßÇïï €§)
+ Check Log Message ${suite.setup[1, 0]} This should be executed in Suite Setup (with ∏ön ÄßÇïï €§)
Continue in for loop
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].kws[0].msgs[0]} ContinuableApocalypseException: 0 FAIL
- Check Log Message ${tc.kws[0].kws[0].kws[1].msgs[0]} This should be executed inside for loop
- Check Log Message ${tc.kws[0].kws[1].kws[0].msgs[0]} ContinuableApocalypseException: 1 FAIL
- Check Log Message ${tc.kws[0].kws[1].kws[1].msgs[0]} This should be executed inside for loop
- Check Log Message ${tc.kws[0].kws[2].kws[0].msgs[0]} ContinuableApocalypseException: 2 FAIL
- Check Log Message ${tc.kws[0].kws[2].kws[1].msgs[0]} This should be executed inside for loop
- Check Log Message ${tc.kws[0].kws[3].kws[0].msgs[0]} ContinuableApocalypseException: 3 FAIL
- Check Log Message ${tc.kws[0].kws[3].kws[1].msgs[0]} This should be executed inside for loop
- Check Log Message ${tc.kws[0].kws[4].kws[0].msgs[0]} ContinuableApocalypseException: 4 FAIL
- Check Log Message ${tc.kws[0].kws[4].kws[1].msgs[0]} This should be executed inside for loop
- Check Log Message ${tc.kws[1].msgs[0]} This should be executed after for loop
+ Check Log Message ${tc[0, 0, 0, 0]} ContinuableApocalypseException: 0 FAIL
+ Check Log Message ${tc[0, 0, 1, 0]} This should be executed inside for loop
+ Check Log Message ${tc[0, 1, 0, 0]} ContinuableApocalypseException: 1 FAIL
+ Check Log Message ${tc[0, 1, 1, 0]} This should be executed inside for loop
+ Check Log Message ${tc[0, 2, 0, 0]} ContinuableApocalypseException: 2 FAIL
+ Check Log Message ${tc[0, 2, 1, 0]} This should be executed inside for loop
+ Check Log Message ${tc[0, 3, 0, 0]} ContinuableApocalypseException: 3 FAIL
+ Check Log Message ${tc[0, 3, 1, 0]} This should be executed inside for loop
+ Check Log Message ${tc[0, 4, 0, 0]} ContinuableApocalypseException: 4 FAIL
+ Check Log Message ${tc[0, 4, 1, 0]} This should be executed inside for loop
+ Check Log Message ${tc[1, 0]} This should be executed after for loop
Continuable and regular failure in for loop
Check Test Case ${TESTNAME}
@@ -102,9 +102,9 @@ robot.api.ContinuableFailure
*** Keywords ***
Verify all failures in user keyword [Arguments] ${kw} ${where}
- Check Log Message ${kw.kws[0].msgs[0]} ContinuableApocalypseException: 1 FAIL
- Check Log Message ${kw.kws[1].msgs[0]} This should be executed in ${where} (with ∏ön ÄßÇïï €§)
- Check Log Message ${kw.kws[2].msgs[0]} ContinuableApocalypseException: 2 FAIL
- Check Log Message ${kw.kws[3].msgs[0]} This should also be executed in ${where}
- Check Log Message ${kw.kws[4].msgs[0]} ContinuableApocalypseException: 3 FAIL
- Check Log Message ${kw.kws[5].msgs[0]} This too should also be executed in ${where}
+ Check Log Message ${kw[0, 0]} ContinuableApocalypseException: 1 FAIL
+ Check Log Message ${kw[1, 0]} This should be executed in ${where} (with ∏ön ÄßÇïï €§)
+ Check Log Message ${kw[2, 0]} ContinuableApocalypseException: 2 FAIL
+ Check Log Message ${kw[3, 0]} This should also be executed in ${where}
+ Check Log Message ${kw[4, 0]} ContinuableApocalypseException: 3 FAIL
+ Check Log Message ${kw[5, 0]} This too should also be executed in ${where}
diff --git a/atest/robot/running/detect_recursion.robot b/atest/robot/running/detect_recursion.robot
new file mode 100644
index 00000000000..cd50dc5d443
--- /dev/null
+++ b/atest/robot/running/detect_recursion.robot
@@ -0,0 +1,41 @@
+*** Settings ***
+Suite Setup Run Tests ${EMPTY} running/detect_recursion.robot
+Library GetNestingLevel.py
+Resource atest_resource.robot
+
+*** Test Cases ***
+Infinite recursion
+ Check Test Case ${TESTNAME}
+
+Infinite cyclic recursion
+ Check Test Case ${TESTNAME}
+
+Infinite recursion with Run Keyword
+ Check Test Case ${TESTNAME}
+
+Infinitely recursive for loop
+ Check Test Case ${TESTNAME}
+
+Recursion below the recursion limit is ok
+ [Documentation] Also verifies that recursion limit blown earlier doesn't affect subsequent tests
+ Check Test Case ${TESTNAME}
+
+Recursion limit is over 140 started keywords
+ ${tc} = Check Test Case Infinite recursion
+ ${level} = Get Nesting Level ${tc}
+ Should Be True 140 < ${level} < 160
+
+Recursion limit can be raised with `sys.setrecursionlimit`
+ [Setup] Should Be True sys.getrecursionlimit() == 1000
+ # Raise limit with executed tests using sitecustomize.py.
+ Create File %{TEMPDIR}/sitecustomize.py import sys; sys.setrecursionlimit(1500)
+ Set Environment Variable PYTHONPATH %{TEMPDIR}
+ # Also raise limit here to be able to process created outputs.
+ Evaluate sys.setrecursionlimit(1500)
+ Run Tests -t "Infinite recursion" running/detect_recursion.robot
+ ${tc} = Check Test Case Infinite recursion
+ ${level} = Get Nesting Level ${tc}
+ Should Be True 220 < ${level} < 240
+ [Teardown] Run Keywords
+ ... Remove File %{TEMPDIR}/sitecustomize.py AND
+ ... Evaluate sys.setrecursionlimit(1000)
diff --git a/atest/robot/running/duplicate_test_name.robot b/atest/robot/running/duplicate_test_name.robot
index 8996cfaf565..68133471a24 100644
--- a/atest/robot/running/duplicate_test_name.robot
+++ b/atest/robot/running/duplicate_test_name.robot
@@ -3,24 +3,35 @@ Suite Setup Run Tests --exclude exclude running/duplicate_test_name.
Resource atest_resource.robot
*** Test Cases ***
-Tests with same name should be executed
+Tests with same name are executed
Should Contain Tests ${SUITE}
- ... Same Test Multiple Times
- ... Same Test Multiple Times
- ... Same Test Multiple Times
- ... Same Test With Different Case And Spaces
- ... SameTestwith Different CASE and s p a c e s
- ... Same Test In Data But Only One Executed
+ ... Duplicates
+ ... Duplicates
+ ... Duplicates
+ ... Duplicates with different case and spaces
+ ... Duplicates with different CASE ands p a c e s
+ ... Duplicates but only one executed
+ ... Test 1 Test 2 Test 3
+ ... Duplicates after resolving variables
+ ... Duplicates after resolving variables
-There should be warning when multiple tests with same name are executed
- Check Multiple Tests Log Message ${ERRORS[0]} Same Test Multiple Times
- Check Multiple Tests Log Message ${ERRORS[1]} Same Test Multiple Times
- Check Multiple Tests Log Message ${ERRORS[2]} SameTestwith Different CASE and s p a c e s
+There is warning when multiple tests with same name are executed
+ Check Multiple Tests Log Message ${ERRORS[0]} Duplicates
+ Check Multiple Tests Log Message ${ERRORS[1]} Duplicates
+ Check Multiple Tests Log Message ${ERRORS[2]} Duplicates with different CASE ands p a c e s
-There should be no warning when there are multiple tests with same name in data but only one is executed
- ${tc} = Check Test Case Same Test In Data But Only One Executed
- Check Log Message ${tc.kws[0].msgs[0]} This is executed!
- Length Should Be ${ERRORS} 3
+There is warning if names are same after resolving variables
+ Check Multiple Tests Log Message ${ERRORS[3]} Duplicates after resolving variables
+
+There is no warning when there are multiple tests with same name but only one is executed
+ Check Test Case Duplicates but only one executed
+ Length Should Be ${ERRORS} 4
+
+Original name can be same if there is variable and its value changes
+ Check Test Case Test 1
+ Check Test Case Test 2
+ Check Test Case Test 3
+ Length Should Be ${ERRORS} 4
*** Keywords ***
Check Multiple Tests Log Message
diff --git a/atest/robot/running/exit_on_failure_tag.robot b/atest/robot/running/exit_on_failure_tag.robot
new file mode 100644
index 00000000000..f95649083ca
--- /dev/null
+++ b/atest/robot/running/exit_on_failure_tag.robot
@@ -0,0 +1,17 @@
+*** Settings ***
+Suite Setup Run Tests ${EMPTY} running/exit_on_failure_tag.robot
+Resource atest_resource.robot
+
+*** Test Cases ***
+Passing test with the tag has not special effect
+ Check Test Case ${TESTNAME}
+
+Failing test without the tag has no special effect
+ Check Test Case ${TESTNAME}
+
+Failing test with the tag initiates exit-on-failure
+ Check Test Case ${TESTNAME}
+
+Subsequent tests are not run
+ Check Test Case ${TESTNAME} 1
+ Check Test Case ${TESTNAME} 2
diff --git a/atest/robot/running/failures_in_teardown.robot b/atest/robot/running/failures_in_teardown.robot
index 8ab85190675..175af344269 100644
--- a/atest/robot/running/failures_in_teardown.robot
+++ b/atest/robot/running/failures_in_teardown.robot
@@ -6,16 +6,16 @@ Resource atest_resource.robot
*** Test Cases ***
One Failure
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.teardown.kws[1].msgs[0]} This should be executed
+ Check Log Message ${tc.teardown[1, 0]} This should be executed
Multiple Failures
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.teardown.kws[2].msgs[0]} This should also be executed
+ Check Log Message ${tc.teardown[2, 0]} This should also be executed
Failure When Setting Variables
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.teardown.kws[0].msgs[0]} \${ret} = None
- Check Log Message ${tc.teardown.kws[0].msgs[1]} Return values is None FAIL
+ Check Log Message ${tc.teardown[0, 0]} \${ret} = None
+ Check Log Message ${tc.teardown[0, 1]} Return values is None FAIL
Failure In For Loop
Check Test Case ${TESTNAME}
@@ -26,43 +26,63 @@ Execution Continues After Test Timeout
Execution Stops After Keyword Timeout
${tc} = Check Test Case ${TESTNAME}
- Length Should Be ${tc.teardown.kws} 2
- Should Be Equal ${tc.teardown.kws[-1].status} NOT RUN
+ Length Should Be ${tc.teardown.body} 2
+ Should Be Equal ${tc.teardown[-1].status} NOT RUN
-Execution Continues After Keyword Timeout Occurs In Executed Keyword
+Execution continues if executed keyword fails for keyword timeout
${tc} = Check Test Case ${TESTNAME}
- Length Should Be ${tc.teardown.body} 2
- Length Should Be ${tc.teardown.body[0].body} 2
- Should Be Equal ${tc.teardown.body[0].body[0].status} FAIL
- Should Be Equal ${tc.teardown.body[0].body[1].status} NOT RUN
- Should Be Equal ${tc.teardown.body[0].status} FAIL
- Should Be Equal ${tc.teardown.body[1].status} FAIL
+ Length Should Be ${tc.teardown.body} 2
+ Should Be Equal ${tc.teardown.body[0].status} FAIL
+ Should Be Equal ${tc.teardown.body[1].status} FAIL
+ Length Should Be ${tc.teardown.body[0].body} 2
+ Should Be Equal ${tc.teardown[0, 0].status} FAIL
+ Check Log Message ${tc.teardown}[0, 0, 0] Keyword timeout 42 milliseconds exceeded. FAIL
+ Should Be Equal ${tc.teardown[0, 1].status} NOT RUN
+ Length Should Be ${tc.teardown.body[1].body} 1
+ Check Log Message ${tc.teardown}[1, 0] This should be executed FAIL
+
+Execution stops after keyword timeout if keyword uses WUKS
+ ${tc} = Check Test Case ${TESTNAME}
+ Length Should Be ${tc.teardown.body} 2
+ Should Be Equal ${tc.teardown.body[0].status} FAIL
+ Should Be Equal ${tc.teardown.body[1].status} NOT RUN
+ Length Should Be ${tc.teardown.body[0].body} 2
+ Should Be Equal ${tc.teardown[0, 0].status} FAIL
+ Should Be Equal ${tc.teardown[0, 1].status} FAIL
+ Length Should Be ${tc.teardown[0, 0].body} 2
+ Should Be Equal ${tc.teardown[0, 0, 0].status} PASS
+ Should Be Equal ${tc.teardown[0, 0, 1].status} FAIL
+ Check Log Message ${tc.teardown}[0, 0, 1, 0] Failing! FAIL
+ Length Should Be ${tc.teardown[0, 1].body} 2
+ Should Be Equal ${tc.teardown[0, 1, 0].status} FAIL
+ Check Log Message ${tc.teardown}[0, 1, 0, 0] Keyword timeout 100 milliseconds exceeded. FAIL
+ Should Be Equal ${tc.teardown[0, 1, 1].status} NOT RUN
Execution Continues If Variable Does Not Exist
${tc} = Check Test Case ${TESTNAME}
- Length Should Be ${tc.teardown.kws} 3
+ Length Should Be ${tc.teardown.body} 3
Execution Continues After Keyword Errors
${tc} = Check Test Case ${TESTNAME}
- Length Should Be ${tc.teardown.kws} 3
+ Length Should Be ${tc.teardown.body} 3
Execution Stops After Syntax Error
${tc} = Check Test Case ${TESTNAME}
- Length Should Be ${tc.teardown.kws} 2
- Should Be Equal ${tc.teardown.kws[-1].status} NOT RUN
+ Length Should Be ${tc.teardown.body} 2
+ Should Be Equal ${tc.teardown[-1].status} NOT RUN
Fatal Error
${tc} = Check Test Case ${TESTNAME} 1
- Length Should Be ${tc.teardown.kws} 2
- Should Be Equal ${tc.teardown.kws[-1].status} NOT RUN
- Check Test Case ${TESTNAME} 2
+ Length Should Be ${tc.teardown.body} 2
+ Should Be Equal ${tc.teardown[-1].status} NOT RUN
+ Check Test Case ${TESTNAME} 2
Suite Teardown Is Executed Fully
${td} = Set Variable ${SUITE.teardown}
- Check Log Message ${td.kws[0].msgs[0]} Suite Message 1 FAIL
- Check Log Message ${td.kws[1].msgs[0]} Suite Message 2 (with ∏ön ÄßÇïï €§) FAIL
- Check Log Message ${td.kws[2].msgs[0]} Variable '\${it is ok not to exist}' not found. FAIL
- Check Log Message ${td.kws[3].msgs[0]} This should be executed
+ Check Log Message ${td[0, 0]} Suite Message 1 FAIL
+ Check Log Message ${td[1, 0]} Suite Message 2 (with ∏ön ÄßÇïï €§) FAIL
+ Check Log Message ${td[2, 0]} Variable '\${it is ok not to exist}' not found. FAIL
+ Check Log Message ${td[3, 0]} This should be executed
${msg} = Catenate SEPARATOR=\n\n
... Suite teardown failed:\nSeveral failures occurred:
... 1) Suite Message 1
@@ -73,5 +93,5 @@ Suite Teardown Is Executed Fully
Suite Teardown Should Stop At Fatal Error
Run Tests ${EMPTY} running/fatal_error_in_suite_teardown.robot
${ts} = Get Test Suite fatal error in suite teardown
- Length Should Be ${ts.teardown.kws} 2
- Should Be Equal ${ts.teardown.kws[-1].status} NOT RUN
+ Length Should Be ${ts.teardown.body} 2
+ Should Be Equal ${ts.teardown[-1].status} NOT RUN
diff --git a/atest/robot/running/fatal_exception.robot b/atest/robot/running/fatal_exception.robot
index 7166f538ad2..6c3898b42df 100644
--- a/atest/robot/running/fatal_exception.robot
+++ b/atest/robot/running/fatal_exception.robot
@@ -5,7 +5,7 @@ Resource atest_resource.robot
Exit From Python Keyword
Run Tests ${EMPTY} running/fatal_exception/01__python_library_kw.robot
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.teardown.msgs[0]} This should be executed
+ Check Log Message ${tc.teardown[0]} This should be executed
Check Test Case Test That Should Not Be Run 1
robot.api.FatalError
@@ -42,7 +42,7 @@ Multiple Suite Aware Exiting From Suite Setup
Run Tests ${EMPTY} running/fatal_exception_suite_setup/
Check Test Case Test That Should Not Be Run 1
${ts1} = Get Test Suite Suite Setup
- Should End With ${ts1.teardown.msgs[0].message} Tearing down 1
+ Should End With ${ts1.teardown[0].message} Tearing down 1
Check Test Case Test That Should Not Be Run 2.1
Check Test Case Test That Should Not Be Run 2.2
${ts2} = Get Test Suite Irrelevant
diff --git a/atest/robot/running/flatten.robot b/atest/robot/running/flatten.robot
index d9fe596666d..dd3ab863fd9 100644
--- a/atest/robot/running/flatten.robot
+++ b/atest/robot/running/flatten.robot
@@ -5,25 +5,28 @@ Resource atest_resource.robot
*** Test Cases ***
A single user keyword
${tc}= User keyword content should be flattened 1
- Check Log Message ${tc.body[0].messages[0]} From the main kw
+ Check Log Message ${tc[0, 0]} From the main kw
Nested UK
${tc}= User keyword content should be flattened 2
- Check Log Message ${tc.body[0].messages[0]} arg
- Check Log Message ${tc.body[0].messages[1]} from nested kw
+ Check Log Message ${tc[0, 0]} arg
+ Check Log Message ${tc[0, 1]} from nested kw
Loops and stuff
- ${tc}= User keyword content should be flattened 10
- Check Log Message ${tc.body[0].messages[0]} inside for 0
- Check Log Message ${tc.body[0].messages[1]} inside for 1
- Check Log Message ${tc.body[0].messages[2]} inside for 2
- Check Log Message ${tc.body[0].messages[3]} inside while 0
- Check Log Message ${tc.body[0].messages[4]} inside while 1
- Check Log Message ${tc.body[0].messages[5]} inside while 2
- Check Log Message ${tc.body[0].messages[6]} inside if
- Check Log Message ${tc.body[0].messages[7]} fail inside try FAIL
- Check Log Message ${tc.body[0].messages[8]} Traceback (most recent call last):* DEBUG pattern=True
- Check Log Message ${tc.body[0].messages[9]} inside except
+ ${tc}= User keyword content should be flattened 13
+ Check Log Message ${tc[0, 0]} inside for 0
+ Check Log Message ${tc[0, 1]} inside for 1
+ Check Log Message ${tc[0, 2]} inside for 2
+ Check Log Message ${tc[0, 3]} inside while 0
+ Check Log Message ${tc[0, 4]} \${LIMIT} = 1
+ Check Log Message ${tc[0, 5]} inside while 1
+ Check Log Message ${tc[0, 6]} \${LIMIT} = 2
+ Check Log Message ${tc[0, 7]} inside while 2
+ Check Log Message ${tc[0, 8]} \${LIMIT} = 3
+ Check Log Message ${tc[0, 9]} inside if
+ Check Log Message ${tc[0, 10]} fail inside try FAIL
+ Check Log Message ${tc[0, 11]} Traceback (most recent call last):* DEBUG pattern=True
+ Check Log Message ${tc[0, 12]} inside except
Recursion
User keyword content should be flattened 8
@@ -34,15 +37,15 @@ Listener methods start and end keyword are called
Log levels
Run Tests ${EMPTY} running/flatten.robot
${tc}= User keyword content should be flattened 4
- Check Log Message ${tc.body[0].messages[0]} INFO 1
- Check Log Message ${tc.body[0].messages[1]} Log level changed from INFO to DEBUG. DEBUG
- Check Log Message ${tc.body[0].messages[2]} INFO 2
- Check Log Message ${tc.body[0].messages[3]} DEBUG 2 level=DEBUG
+ Check Log Message ${tc[0, 0]} INFO 1
+ Check Log Message ${tc[0, 1]} Log level changed from INFO to DEBUG. DEBUG
+ Check Log Message ${tc[0, 2]} INFO 2
+ Check Log Message ${tc[0, 3]} DEBUG 2 level=DEBUG
*** Keywords ***
User keyword content should be flattened
[Arguments] ${expected_message_count}=0
${tc}= Check Test Case ${TESTNAME}
- Length Should Be ${tc.body[0].body} ${expected_message_count}
- Length Should Be ${tc.body[0].messages} ${expected_message_count}
+ Length Should Be ${tc[0].body} ${expected_message_count}
+ Length Should Be ${tc[0].messages} ${expected_message_count}
RETURN ${tc}
diff --git a/atest/robot/running/for/continue_for_loop.robot b/atest/robot/running/for/continue_for_loop.robot
index 4b4cc9f3c8a..59ab08e67b4 100644
--- a/atest/robot/running/for/continue_for_loop.robot
+++ b/atest/robot/running/for/continue_for_loop.robot
@@ -26,8 +26,8 @@ Continue For Loop In User Keyword Without For Loop Should Fail
Continue For Loop Keyword Should Log Info
${tc} = Check Test Case Simple Continue For Loop
- Should Be Equal ${tc.kws[0].kws[0].kws[0].full_name} BuiltIn.Continue For Loop
- Check Log Message ${tc.kws[0].kws[0].kws[0].msgs[0]} Continuing for loop from the next iteration.
+ Should Be Equal ${tc[0, 0, 0].full_name} BuiltIn.Continue For Loop
+ Check Log Message ${tc[0, 0, 0, 0]} Continuing for loop from the next iteration.
Continue For Loop In Test Teardown
Test And All Keywords Should Have Passed
diff --git a/atest/robot/running/for/exit_for_loop.robot b/atest/robot/running/for/exit_for_loop.robot
index 127d8708d00..aff02d26484 100644
--- a/atest/robot/running/for/exit_for_loop.robot
+++ b/atest/robot/running/for/exit_for_loop.robot
@@ -29,8 +29,8 @@ Exit For Loop In User Keyword Without For Loop Should Fail
Exit For Loop Keyword Should Log Info
${tc} = Check Test Case Simple Exit For Loop
- Should Be Equal ${tc.kws[0].kws[0].kws[0].full_name} BuiltIn.Exit For Loop
- Check Log Message ${tc.kws[0].kws[0].kws[0].msgs[0]} Exiting for loop altogether.
+ Should Be Equal ${tc[0, 0, 0].full_name} BuiltIn.Exit For Loop
+ Check Log Message ${tc[0, 0, 0, 0]} Exiting for loop altogether.
Exit For Loop In Test Teardown
Test And All Keywords Should Have Passed
diff --git a/atest/robot/running/for/for.resource b/atest/robot/running/for/for.resource
index 9a71e966d7d..9b29093eda6 100644
--- a/atest/robot/running/for/for.resource
+++ b/atest/robot/running/for/for.resource
@@ -10,21 +10,21 @@ Check test and get loop
Check test and failed loop
[Arguments] ${test name} ${type}=FOR ${loop index}=0 &{config}
${loop} = Check test and get loop ${test name} ${loop index}
- Length Should Be ${loop.body} 2
- Should Be Equal ${loop.body[0].type} ITERATION
- Should Be Equal ${loop.body[1].type} MESSAGE
+ Length Should Be ${loop.body} 2
+ Should Be Equal ${loop[0].type} ITERATION
+ Should Be Equal ${loop[1].type} MESSAGE
Run Keyword Should Be ${type} loop ${loop} 1 FAIL &{config}
Should be FOR loop
[Arguments] ${loop} ${iterations} ${status}=PASS ${flavor}=IN
... ${start}=${None} ${mode}=${None} ${fill}=${None}
- Should Be Equal ${loop.type} FOR
- Should Be Equal ${loop.flavor} ${flavor}
- Should Be Equal ${loop.start} ${start}
- Should Be Equal ${loop.mode} ${mode}
- Should Be Equal ${loop.fill} ${fill}
- Length Should Be ${loop.body.filter(messages=False)} ${iterations}
- Should Be Equal ${loop.status} ${status}
+ Should Be Equal ${loop.type} FOR
+ Should Be Equal ${loop.flavor} ${flavor}
+ Should Be Equal ${loop.start} ${start}
+ Should Be Equal ${loop.mode} ${mode}
+ Should Be Equal ${loop.fill} ${fill}
+ Length Should Be ${loop.non_messages} ${iterations}
+ Should Be Equal ${loop.status} ${status}
Should be IN RANGE loop
[Arguments] ${loop} ${iterations} ${status}=PASS
diff --git a/atest/robot/running/for/for.robot b/atest/robot/running/for/for.robot
index 0d7a2ec21e1..93e7769b44b 100644
--- a/atest/robot/running/for/for.robot
+++ b/atest/robot/running/for/for.robot
@@ -1,28 +1,28 @@
*** Settings ***
-Suite Setup Run Tests ${EMPTY} running/for/for.robot
+Suite Setup Run Tests --log log-tests-also-string-reprs.html running/for/for.robot
+Suite Teardown File Should Exist ${OUTDIR}/log-tests-also-string-reprs.html
Resource for.resource
*** Test Cases ***
Simple loop
- ${tc} = Check test case ${TEST NAME}
- ${loop} = Set variable ${tc.body[1]}
- Check log message ${tc.body[0].msgs[0]} Not yet in FOR
- Should be FOR loop ${loop} 2
- Should be FOR iteration ${loop.body[0]} \${var}=one
- Check log message ${loop.body[0].body[0].msgs[0]} var: one
- Should be FOR iteration ${loop.body[1]} \${var}=two
- Check log message ${loop.body[1].body[0].msgs[0]} var: two
- Check log message ${tc.body[2].body[0]} Not in FOR anymore
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Log Message ${tc[0, 0]} Not yet in FOR
+ Should be FOR loop ${tc[1]} 2
+ Should be FOR iteration ${tc[1, 0]} \${var}=one
+ Check Log Message ${tc[1, 0, 0, 0]} var: one
+ Should be FOR iteration ${tc[1, 1]} \${var}=two
+ Check Log Message ${tc[1, 1, 0, 0]} var: two
+ Check Log Message ${tc[2, 0]} Not in FOR anymore
Variables in values
${loop} = Check test and get loop ${TEST NAME}
- Should be FOR loop ${loop} 6
- "Variables in values" helper ${loop.kws[0]} 1
- "Variables in values" helper ${loop.kws[1]} 2
- "Variables in values" helper ${loop.kws[2]} 3
- "Variables in values" helper ${loop.kws[3]} 4
- "Variables in values" helper ${loop.kws[4]} 5
- "Variables in values" helper ${loop.kws[5]} 6
+ Should be FOR loop ${loop} 6
+ "Variables in values" helper ${loop[0]} 1
+ "Variables in values" helper ${loop[1]} 2
+ "Variables in values" helper ${loop[2]} 3
+ "Variables in values" helper ${loop[3]} 4
+ "Variables in values" helper ${loop[4]} 5
+ "Variables in values" helper ${loop[5]} 6
Indentation is not required
${loop} = Check test and get loop ${TEST NAME} 1
@@ -30,203 +30,203 @@ Indentation is not required
Values on multiple rows
${loop} = Check test and get loop ${TEST NAME}
- Should be FOR loop ${loop} 10
- Check log message ${loop.kws[0].kws[0].msgs[0]} 1
+ Should be FOR loop ${loop} 10
+ Check Log Message ${loop[0, 0, 0]} 1
FOR ${i} IN RANGE 10
- Check log message ${loop.kws[${i}].kws[0].msgs[0]} ${{str($i + 1)}}
+ Check Log Message ${loop[${i}, 0, 0]} ${{str($i + 1)}}
END
# Sanity check
- Check log message ${loop.kws[0].kws[0].msgs[0]} 1
- Check log message ${loop.kws[4].kws[0].msgs[0]} 5
- Check log message ${loop.kws[9].kws[0].msgs[0]} 10
+ Check Log Message ${loop[0, 0, 0]} 1
+ Check Log Message ${loop[4, 0, 0]} 5
+ Check Log Message ${loop[9, 0, 0]} 10
Keyword arguments on multiple rows
${loop} = Check test and get loop ${TEST NAME}
- Should be FOR loop ${loop} 2
- Check log message ${loop.kws[0].kws[1].msgs[0]} 1 2 3 4 5 6 7 one
- Check log message ${loop.kws[1].kws[1].msgs[0]} 1 2 3 4 5 6 7 two
+ Should be FOR loop ${loop} 2
+ Check Log Message ${loop[0, 1, 0]} 1 2 3 4 5 6 7 one
+ Check Log Message ${loop[1, 1, 0]} 1 2 3 4 5 6 7 two
Multiple loops in a test
- ${tc} = Check test case ${TEST NAME}
- Should be FOR loop ${tc.kws[0]} 2
- Check log message ${tc.kws[0].kws[0].kws[0].msgs[0]} In first loop with "foo"
- Check log message ${tc.kws[0].kws[1].kws[0].msgs[0]} In first loop with "bar"
- Should be FOR loop ${tc.kws[1]} 1
- Check kw "My UK 2" ${tc.kws[1].kws[0].kws[0]} Hello, world!
- Check log message ${tc.kws[2].msgs[0]} Outside loop
- Should be FOR loop ${tc.kws[3]} 2
- Check log message ${tc.kws[3].kws[0].kws[0].msgs[0]} Third loop
- Check log message ${tc.kws[3].kws[0].kws[2].msgs[0]} Value: a
- Check log message ${tc.kws[3].kws[1].kws[0].msgs[0]} Third loop
- Check log message ${tc.kws[3].kws[1].kws[2].msgs[0]} Value: b
- Check log message ${tc.kws[4].msgs[0]} The End
+ ${tc} = Check Test Case ${TEST NAME}
+ Should be FOR loop ${tc[0]} 2
+ Check Log Message ${tc[0, 0, 0, 0]} In first loop with "foo"
+ Check Log Message ${tc[0, 1, 0, 0]} In first loop with "bar"
+ Should be FOR loop ${tc[1]} 1
+ Check kw "My UK 2" ${tc[1, 0, 0]} Hello, world!
+ Check Log Message ${tc[2, 0]} Outside loop
+ Should be FOR loop ${tc[3]} 2
+ Check Log Message ${tc[3, 0, 0, 0]} Third loop
+ Check Log Message ${tc[3, 0, 2, 0]} Value: a
+ Check Log Message ${tc[3, 1, 0, 0]} Third loop
+ Check Log Message ${tc[3, 1, 2, 0]} Value: b
+ Check Log Message ${tc[4, 0]} The End
Nested loop syntax
- ${tc} = Check test case ${TEST NAME}
- Should be FOR loop ${tc.kws[0]} 3
- Should be FOR loop ${tc.kws[0].kws[0].kws[1]} 3
- Check log message ${tc.kws[0].kws[0].kws[0].msgs[0]} 1 in
- Check log message ${tc.kws[0].kws[0].kws[1].kws[0].kws[0].msgs[0]} values 1 a
- Check log message ${tc.kws[0].kws[0].kws[1].kws[1].kws[0].msgs[0]} values 1 b
- Check log message ${tc.kws[0].kws[0].kws[1].kws[2].kws[0].msgs[0]} values 1 c
- Check log message ${tc.kws[0].kws[0].kws[2].msgs[0]} 1 out
- Check log message ${tc.kws[0].kws[1].kws[0].msgs[0]} 2 in
- Check log message ${tc.kws[0].kws[1].kws[1].kws[0].kws[0].msgs[0]} values 2 a
- Check log message ${tc.kws[0].kws[1].kws[1].kws[1].kws[0].msgs[0]} values 2 b
- Check log message ${tc.kws[0].kws[1].kws[1].kws[2].kws[0].msgs[0]} values 2 c
- Check log message ${tc.kws[0].kws[1].kws[2].msgs[0]} 2 out
- Check log message ${tc.kws[0].kws[2].kws[0].msgs[0]} 3 in
- Check log message ${tc.kws[0].kws[2].kws[1].kws[0].kws[0].msgs[0]} values 3 a
- Check log message ${tc.kws[0].kws[2].kws[1].kws[1].kws[0].msgs[0]} values 3 b
- Check log message ${tc.kws[0].kws[2].kws[1].kws[2].kws[0].msgs[0]} values 3 c
- Check log message ${tc.kws[0].kws[2].kws[2].msgs[0]} 3 out
- Check log message ${tc.kws[1].msgs[0]} The End
+ ${tc} = Check Test Case ${TEST NAME}
+ Should be FOR loop ${tc[0]} 3
+ Should be FOR loop ${tc[0, 0, 1]} 3
+ Check Log Message ${tc[0, 0, 0, 0]} 1 in
+ Check Log Message ${tc[0, 0, 1, 0, 0, 0]} values 1 a
+ Check Log Message ${tc[0, 0, 1, 1, 0, 0]} values 1 b
+ Check Log Message ${tc[0, 0, 1, 2, 0, 0]} values 1 c
+ Check Log Message ${tc[0, 0, 2, 0]} 1 out
+ Check Log Message ${tc[0, 1, 0, 0]} 2 in
+ Check Log Message ${tc[0, 1, 1, 0, 0, 0]} values 2 a
+ Check Log Message ${tc[0, 1, 1, 1, 0, 0]} values 2 b
+ Check Log Message ${tc[0, 1, 1, 2, 0, 0]} values 2 c
+ Check Log Message ${tc[0, 1, 2, 0]} 2 out
+ Check Log Message ${tc[0, 2, 0, 0]} 3 in
+ Check Log Message ${tc[0, 2, 1, 0, 0, 0]} values 3 a
+ Check Log Message ${tc[0, 2, 1, 1, 0, 0]} values 3 b
+ Check Log Message ${tc[0, 2, 1, 2, 0, 0]} values 3 c
+ Check Log Message ${tc[0, 2, 2, 0]} 3 out
+ Check Log Message ${tc[1, 0]} The End
Multiple loops in a loop
- Check test case ${TEST NAME}
+ Check Test Case ${TEST NAME}
Deeply nested loops
- Check test case ${TEST NAME}
+ Check Test Case ${TEST NAME}
Settings after FOR
- ${tc} = Check test case ${TEST NAME}
- Should be FOR loop ${tc.kws[0]} 1
- Check log message ${tc.teardown.msgs[0]} Teardown was found and eXecuted.
+ ${tc} = Check Test Case ${TEST NAME}
+ Should be FOR loop ${tc[0]} 1
+ Check Log Message ${tc.teardown[0]} Teardown was found and eXecuted.
Looping over empty list variable is OK
- ${tc} = Check test case ${TEST NAME}
- Should be FOR loop ${tc.kws[0]} 1 NOT RUN
- Should be FOR iteration ${tc.body[0].body[0]} \${var}=
- Check keyword data ${tc.body[0].body[0].body[0]} BuiltIn.Fail args=Not executed status=NOT RUN
+ ${tc} = Check Test Case ${TEST NAME}
+ Should be FOR loop ${tc[0]} 1 NOT RUN
+ Should be FOR iteration ${tc[0, 0]} \${var}=
+ Check keyword data ${tc[0, 0, 0]} BuiltIn.Fail args=Not executed status=NOT RUN
Other iterables
- ${tc} = Check test case ${TEST NAME}
- Should be FOR loop ${tc.kws[2]} 10
+ ${tc} = Check Test Case ${TEST NAME}
+ Should be FOR loop ${tc[2]} 10
Failure inside FOR
${loop} = Check test and get loop ${TEST NAME} 1
- Should be FOR loop ${loop} 1 FAIL
- Check log message ${loop.kws[0].kws[0].msgs[0]} Hello before failing kw
- Should be equal ${loop.kws[0].kws[0].status} PASS
- Check log message ${loop.kws[0].kws[1].msgs[0]} Here we fail! FAIL
- Should be equal ${loop.kws[0].kws[1].status} FAIL
- Should be equal ${loop.kws[0].kws[2].status} NOT RUN
- Should be equal ${loop.kws[0].status} FAIL
- Length should be ${loop.kws[0].kws} 3
+ Should be FOR loop ${loop} 1 FAIL
+ Check Log Message ${loop[0, 0, 0]} Hello before failing kw
+ Should Be Equal ${loop[0, 0].status} PASS
+ Check Log Message ${loop[0, 1, 0]} Here we fail! FAIL
+ Should Be Equal ${loop[0, 1].status} FAIL
+ Should Be Equal ${loop[0, 2].status} NOT RUN
+ Should Be Equal ${loop[0].status} FAIL
+ Length Should Be ${loop[0].body} 3
${loop} = Check test and get loop ${TEST NAME} 2
- Should be FOR loop ${loop} 4 FAIL
- Check log message ${loop.kws[0].kws[0].msgs[0]} Before Check
- Check log message ${loop.kws[0].kws[2].msgs[0]} After Check
- Length should be ${loop.kws[0].kws} 3
- Should be equal ${loop.kws[0].status} PASS
- Should be equal ${loop.kws[1].status} PASS
- Should be equal ${loop.kws[2].status} PASS
- Check log message ${loop.kws[3].kws[0].msgs[0]} Before Check
- Check log message ${loop.kws[3].kws[1].msgs[0]} Failure with <4> FAIL
- Should be equal ${loop.kws[3].kws[2].status} NOT RUN
- Length should be ${loop.kws[3].kws} 3
- Should be equal ${loop.kws[3].status} FAIL
+ Should be FOR loop ${loop} 4 FAIL
+ Check Log Message ${loop[0, 0, 0]} Before Check
+ Check Log Message ${loop[0, 2, 0]} After Check
+ Length Should Be ${loop[0].body} 3
+ Should Be Equal ${loop[0].status} PASS
+ Should Be Equal ${loop[1].status} PASS
+ Should Be Equal ${loop[2].status} PASS
+ Check Log Message ${loop[3, 0, 0]} Before Check
+ Check Log Message ${loop[3, 1, 0]} Failure with <4> FAIL
+ Should Be Equal ${loop[3, 2].status} NOT RUN
+ Length Should Be ${loop[3].body} 3
+ Should Be Equal ${loop[3].status} FAIL
Loop with user keywords
${loop} = Check test and get loop ${TEST NAME}
Should be FOR loop ${loop} 2
- Check kw "My UK" ${loop.kws[0].kws[0]}
- Check kw "My UK 2" ${loop.kws[0].kws[1]} foo
- Check kw "My UK" ${loop.kws[1].kws[0]}
- Check kw "My UK 2" ${loop.kws[1].kws[1]} bar
+ Check kw "My UK" ${loop[0, 0]}
+ Check kw "My UK 2" ${loop[0, 1]} foo
+ Check kw "My UK" ${loop[1, 0]}
+ Check kw "My UK 2" ${loop[1, 1]} bar
Loop with failures in user keywords
- ${tc} = Check test case ${TEST NAME}
- Should be FOR loop ${tc.kws[0]} 2 FAIL
+ ${tc} = Check Test Case ${TEST NAME}
+ Should be FOR loop ${tc[0]} 2 FAIL
Loop in user keyword
- ${tc} = Check test case ${TEST NAME}
- Check kw "For In UK" ${tc.kws[0]}
- Check kw "For In UK with Args" ${tc.kws[1]} 4 one
+ ${tc} = Check Test Case ${TEST NAME}
+ Check kw "For In UK" ${tc[0]}
+ Check kw "For In UK with Args" ${tc[1]} 4 one
Keyword with loop calling other keywords with loops
- ${tc} = Check test case ${TEST NAME}
- Check kw "Nested For In UK" ${tc.kws[0]} foo
+ ${tc} = Check Test Case ${TEST NAME}
+ Check kw "Nested For In UK" ${tc[0]} foo
Test with loop calling keywords with loops
${loop} = Check test and get loop ${TEST NAME} 1
- Should be FOR loop ${loop} 1 FAIL
- Check kw "For In UK" ${loop.kws[0].kws[0]}
- Check kw "For In UK with Args" ${loop.kws[0].kws[1]} 2 one
- Check kw "Nested For In UK" ${loop.kws[0].kws[2]} one
+ Should be FOR loop ${loop} 1 FAIL
+ Check kw "For In UK" ${loop[0, 0]}
+ Check kw "For In UK with Args" ${loop[0, 1]} 2 one
+ Check kw "Nested For In UK" ${loop[0, 2]} one
Loop variables is available after loop
- Check test case ${TEST NAME}
+ Check Test Case ${TEST NAME}
Assign inside loop
${loop} = Check test and get loop ${TEST NAME}
Should be FOR loop ${loop} 2
- Check log message ${loop.kws[0].kws[0].msgs[0]} \${v1} = v1
- Check log message ${loop.kws[0].kws[1].msgs[0]} \${v2} = v2
- Check log message ${loop.kws[0].kws[1].msgs[1]} \${v3} = vY
- Check log message ${loop.kws[0].kws[2].msgs[0]} \@{list} = [ v1 | v2 | vY | Y ]
- Check log message ${loop.kws[1].kws[0].msgs[0]} \${v1} = v1
- Check log message ${loop.kws[1].kws[1].msgs[0]} \${v2} = v2
- Check log message ${loop.kws[1].kws[1].msgs[1]} \${v3} = vZ
- Check log message ${loop.kws[1].kws[2].msgs[0]} \@{list} = [ v1 | v2 | vZ | Z ]
+ Check Log Message ${loop[0, 0, 0]} \${v1} = v1
+ Check Log Message ${loop[0, 1, 0]} \${v2} = v2
+ Check Log Message ${loop[0, 1, 1]} \${v3} = vY
+ Check Log Message ${loop[0, 2, 0]} \@{list} = [ v1 | v2 | vY | Y ]
+ Check Log Message ${loop[1, 0, 0]} \${v1} = v1
+ Check Log Message ${loop[1, 1, 0]} \${v2} = v2
+ Check Log Message ${loop[1, 1, 1]} \${v3} = vZ
+ Check Log Message ${loop[1, 2, 0]} \@{list} = [ v1 | v2 | vZ | Z ]
Invalid assign inside loop
- ${tc} = Check test case ${TEST NAME}
- Should be FOR loop ${tc.kws[0]} 1 FAIL
+ ${tc} = Check Test Case ${TEST NAME}
+ Should be FOR loop ${tc[0]} 1 FAIL
Loop with non-existing keyword
- Check test case ${TEST NAME}
+ Check Test Case ${TEST NAME}
Loop with non-existing variable
- Check test case ${TEST NAME}
+ Check Test Case ${TEST NAME}
Loop value with non-existing variable
- Check test case ${TEST NAME}
+ Check Test Case ${TEST NAME}
Multiple loop variables
${tc} = Check Test Case ${TEST NAME}
- ${loop} = Set Variable ${tc.body[0]}
- Should be FOR loop ${loop} 4
- Should be FOR iteration ${loop.body[0]} \${x}=1 \${y}=a
- Check log message ${loop.body[0].body[0].msgs[0]} 1a
- Should be FOR iteration ${loop.body[1]} \${x}=2 \${y}=b
- Check log message ${loop.body[1].body[0].msgs[0]} 2b
- Should be FOR iteration ${loop.body[2]} \${x}=3 \${y}=c
- Check log message ${loop.body[2].body[0].msgs[0]} 3c
- Should be FOR iteration ${loop.body[3]} \${x}=4 \${y}=d
- Check log message ${loop.body[3].body[0].msgs[0]} 4d
- ${loop} = Set Variable ${tc.body[2]}
- Should be FOR loop ${loop} 2
- Should be FOR iteration ${loop.body[0]} \${a}=1 \${b}=2 \${c}=3 \${d}=4 \${e}=5
- Should be FOR iteration ${loop.body[1]} \${a}=1 \${b}=2 \${c}=3 \${d}=4 \${e}=5
+ ${loop} = Set Variable ${tc[0]}
+ Should be FOR loop ${loop} 4
+ Should be FOR iteration ${loop[0]} \${x}=1 \${y}=a
+ Check Log Message ${loop[0, 0, 0]} 1a
+ Should be FOR iteration ${loop[1]} \${x}=2 \${y}=b
+ Check Log Message ${loop[1, 0, 0]} 2b
+ Should be FOR iteration ${loop[2]} \${x}=3 \${y}=c
+ Check Log Message ${loop[2, 0, 0]} 3c
+ Should be FOR iteration ${loop[3]} \${x}=4 \${y}=d
+ Check Log Message ${loop[3, 0, 0]} 4d
+ ${loop} = Set Variable ${tc[2]}
+ Should be FOR loop ${loop} 2
+ Should be FOR iteration ${loop[0]} \${a}=1 \${b}=2 \${c}=3 \${d}=4 \${e}=5
+ Should be FOR iteration ${loop[1]} \${a}=1 \${b}=2 \${c}=3 \${d}=4 \${e}=5
Wrong number of loop variables
Check test and failed loop ${TEST NAME} 1
Check test and failed loop ${TEST NAME} 2
Cut long iteration variable values
- ${tc} = Check test case ${TEST NAME}
- ${loop} = Set Variable ${tc.body[6]}
+ ${tc} = Check Test Case ${TEST NAME}
+ ${loop} = Set Variable ${tc[6]}
${exp10} = Set Variable 0123456789
${exp100} = Evaluate "${exp10}" * 10
${exp200} = Evaluate "${exp10}" * 20
${exp200+} = Set Variable ${exp200}...
- Should be FOR loop ${loop} 6
- Should be FOR iteration ${loop.body[0]} \${var}=${exp10}
- Should be FOR iteration ${loop.body[1]} \${var}=${exp100}
- Should be FOR iteration ${loop.body[2]} \${var}=${exp200}
- Should be FOR iteration ${loop.body[3]} \${var}=${exp200+}
- Should be FOR iteration ${loop.body[4]} \${var}=${exp200+}
- Should be FOR iteration ${loop.body[5]} \${var}=${exp200+}
- ${loop} = Set Variable ${tc.body[7]}
- Should be FOR loop ${loop} 2
- Should be FOR iteration ${loop.body[0]} \${var1}=${exp10} \${var2}=${exp100} \${var3}=${exp200}
- Should be FOR iteration ${loop.body[1]} \${var1}=${exp200+} \${var2}=${exp200+} \${var3}=${exp200+}
+ Should be FOR loop ${loop} 6
+ Should be FOR iteration ${loop[0]} \${var}=${exp10}
+ Should be FOR iteration ${loop[1]} \${var}=${exp100}
+ Should be FOR iteration ${loop[2]} \${var}=${exp200}
+ Should be FOR iteration ${loop[3]} \${var}=${exp200+}
+ Should be FOR iteration ${loop[4]} \${var}=${exp200+}
+ Should be FOR iteration ${loop[5]} \${var}=${exp200+}
+ ${loop} = Set Variable ${tc[7]}
+ Should be FOR loop ${loop} 2
+ Should be FOR iteration ${loop[0]} \${var1}=${exp10} \${var2}=${exp100} \${var3}=${exp200}
+ Should be FOR iteration ${loop[1]} \${var1}=${exp200+} \${var2}=${exp200+} \${var3}=${exp200+}
Characters that are illegal in XML
- ${tc} = Check test case ${TEST NAME}
- Should be FOR iteration ${tc.body[0].body[0]} \${var}=illegal:
- Should be FOR iteration ${tc.body[0].body[1]} \${var}=more:
+ ${tc} = Check Test Case ${TEST NAME}
+ Should be FOR iteration ${tc[0, 0]} \${var}=illegal:
+ Should be FOR iteration ${tc[0, 1]} \${var}=more:
Old :FOR syntax is not supported
Check Test Case ${TESTNAME}
@@ -235,12 +235,12 @@ Escaping with backslash is not supported
Check Test Case ${TESTNAME}
FOR is case and space sensitive
- Check test case ${TEST NAME} 1
- Check test case ${TEST NAME} 2
+ Check Test Case ${TEST NAME} 1
+ Check Test Case ${TEST NAME} 2
Invalid END usage
- Check test case ${TEST NAME} 1
- Check test case ${TEST NAME} 2
+ Check Test Case ${TEST NAME} 1
+ Check Test Case ${TEST NAME} 2
Empty body
Check test and failed loop ${TEST NAME}
@@ -266,13 +266,13 @@ Invalid loop variable
Check test and failed loop ${TEST NAME} 6
Invalid separator
- Check test case ${TEST NAME}
+ Check Test Case ${TEST NAME}
Separator is case- and space-sensitive
- Check test case ${TEST NAME} 1
- Check test case ${TEST NAME} 2
- Check test case ${TEST NAME} 3
- Check test case ${TEST NAME} 4
+ Check Test Case ${TEST NAME} 1
+ Check Test Case ${TEST NAME} 2
+ Check Test Case ${TEST NAME} 3
+ Check Test Case ${TEST NAME} 4
FOR without any paramenters
Check Test Case ${TESTNAME}
@@ -283,10 +283,10 @@ Syntax error in nested loop
Unexecuted
${tc} = Check Test Case ${TESTNAME}
- Should be FOR loop ${tc.body[1].body[0].body[0]} 1 NOT RUN
- Should be FOR iteration ${tc.body[1].body[0].body[0].body[0]} \${x}= \${y}=
- Should be FOR loop ${tc.body[5]} 1 NOT RUN
- Should be FOR iteration ${tc.body[5].body[0]} \${x}= \${y}=
+ Should be FOR loop ${tc[1, 0, 0]} 1 NOT RUN
+ Should be FOR iteration ${tc[1, 0, 0, 0]} \${x}= \${y}=
+ Should be FOR loop ${tc[5]} 1 NOT RUN
+ Should be FOR iteration ${tc[5, 0]} \${x}= \${y}=
Header at the end of file
Check Test Case ${TESTNAME}
@@ -294,49 +294,49 @@ Header at the end of file
*** Keywords ***
"Variables in values" helper
[Arguments] ${kw} ${num}
- Check log message ${kw.kws[0].msgs[0]} ${num}
- Check log message ${kw.kws[1].msgs[0]} Hello from for loop
- Should be equal ${kw.kws[2].full_name} BuiltIn.No Operation
+ Check Log Message ${kw[0, 0]} ${num}
+ Check Log Message ${kw[1, 0]} Hello from for loop
+ Should Be Equal ${kw[2].full_name} BuiltIn.No Operation
Check kw "My UK"
[Arguments] ${kw}
- Should be equal ${kw.full_name} My UK
- Should be equal ${kw.kws[0].full_name} BuiltIn.No Operation
- Check log message ${kw.kws[1].msgs[0]} We are in My UK
+ Should Be Equal ${kw.full_name} My UK
+ Should Be Equal ${kw[0].full_name} BuiltIn.No Operation
+ Check Log Message ${kw[1, 0]} We are in My UK
Check kw "My UK 2"
[Arguments] ${kw} ${arg}
- Should be equal ${kw.full_name} My UK 2
- Check kw "My UK" ${kw.kws[0]}
- Check log message ${kw.kws[1].msgs[0]} My UK 2 got argument "${arg}"
- Check kw "My UK" ${kw.kws[2]}
+ Should Be Equal ${kw.full_name} My UK 2
+ Check kw "My UK" ${kw[0]}
+ Check Log Message ${kw[1, 0]} My UK 2 got argument "${arg}"
+ Check kw "My UK" ${kw[2]}
Check kw "For In UK"
[Arguments] ${kw}
- Should be equal ${kw.full_name} For In UK
- Check log message ${kw.kws[0].msgs[0]} Not for yet
- Should be FOR loop ${kw.kws[1]} 2
- Check log message ${kw.kws[1].kws[0].kws[0].msgs[0]} This is for with 1
- Check kw "My UK" ${kw.kws[1].kws[0].kws[1]}
- Check log message ${kw.kws[1].kws[1].kws[0].msgs[0]} This is for with 2
- Check kw "My UK" ${kw.kws[1].kws[1].kws[1]}
- Check log message ${kw.kws[2].msgs[0]} Not for anymore
+ Should Be Equal ${kw.full_name} For In UK
+ Check Log Message ${kw[0, 0]} Not for yet
+ Should be FOR loop ${kw[1]} 2
+ Check Log Message ${kw[1, 0, 0, 0]} This is for with 1
+ Check kw "My UK" ${kw[1, 0, 1]}
+ Check Log Message ${kw[1, 1, 0, 0]} This is for with 2
+ Check kw "My UK" ${kw[1, 1, 1]}
+ Check Log Message ${kw[2, 0]} Not for anymore
Check kw "For In UK With Args"
[Arguments] ${kw} ${arg_count} ${first_arg}
- Should be equal ${kw.full_name} For In UK With Args
- Should be FOR loop ${kw.kws[0]} ${arg_count}
- Check kw "My UK 2" ${kw.kws[0].kws[0].kws[0]} ${first_arg}
- Should be FOR loop ${kw.kws[2]} 1
- Check log message ${kw.kws[2].kws[0].kws[0].msgs[0]} This for loop is executed only once
+ Should Be Equal ${kw.full_name} For In UK With Args
+ Should be FOR loop ${kw[0]} ${arg_count}
+ Check kw "My UK 2" ${kw[0, 0, 0]} ${first_arg}
+ Should be FOR loop ${kw[2]} 1
+ Check Log Message ${kw[2, 0, 0, 0]} This for loop is executed only once
Check kw "Nested For In UK"
[Arguments] ${kw} ${first_arg}
- Should be FOR loop ${kw.kws[0]} 1 FAIL
- Check kw "For In UK" ${kw.kws[0].kws[0].kws[0]}
- ${nested2} = Set Variable ${kw.kws[0].kws[0].kws[1]}
- Should be equal ${nested2.full_name} Nested For In UK 2
- Should be FOR loop ${nested2.kws[0]} 2
- Check kw "For In UK" ${nested2.kws[0].kws[0].kws[0]}
- Check log message ${nested2.kws[0].kws[0].kws[1].msgs[0]} Got arg: ${first_arg}
- Check log message ${nested2.kws[1].msgs[0]} This ought to be enough FAIL
+ Should be FOR loop ${kw[0]} 1 FAIL
+ Check kw "For In UK" ${kw[0, 0, 0]}
+ ${nested2} = Set Variable ${kw[0, 0, 1]}
+ Should Be Equal ${nested2.full_name} Nested For In UK 2
+ Should be FOR loop ${nested2[0]} 2
+ Check kw "For In UK" ${nested2[0, 0, 0]}
+ Check Log Message ${nested2[0, 0, 1, 0]} Got arg: ${first_arg}
+ Check Log Message ${nested2[1, 0]} This ought to be enough FAIL
diff --git a/atest/robot/running/for/for_dict_iteration.robot b/atest/robot/running/for/for_dict_iteration.robot
index 52910c9337e..a8b840726fc 100644
--- a/atest/robot/running/for/for_dict_iteration.robot
+++ b/atest/robot/running/for/for_dict_iteration.robot
@@ -72,14 +72,14 @@ Equal sign in variable
... FOR loop iteration over values that are all in 'name=value' format like 'a=1' is deprecated.
... In the future this syntax will mean iterating over names and values separately like when iterating over '\&{dict} variables.
... Escape at least one of the values like 'a\\=1' to use normal FOR loop iteration and to disable this warning.
- Check Log Message ${tc.body[0].msgs[0]} ${message} WARN
- Check Log Message ${ERRORS}[0] ${message} WARN
+ Check Log Message ${tc[0, 0]} ${message} WARN
+ Check Log Message ${ERRORS}[0] ${message} WARN
${message} = Catenate
... FOR loop iteration over values that are all in 'name=value' format like 'x==1' is deprecated.
... In the future this syntax will mean iterating over names and values separately like when iterating over '\&{dict} variables.
... Escape at least one of the values like 'x\\==1' to use normal FOR loop iteration and to disable this warning.
- Check Log Message ${tc.body[2].msgs[0]} ${message} WARN
- Check Log Message ${ERRORS}[1] ${message} WARN
+ Check Log Message ${tc[2, 0]} ${message} WARN
+ Check Log Message ${ERRORS}[1] ${message} WARN
Non-string keys
Check Test Case ${TESTNAME}
diff --git a/atest/robot/running/for/for_in_range.robot b/atest/robot/running/for/for_in_range.robot
index 9c3f2c12a7a..0defe65d78d 100644
--- a/atest/robot/running/for/for_in_range.robot
+++ b/atest/robot/running/for/for_in_range.robot
@@ -5,65 +5,65 @@ Resource for.resource
*** Test Cases ***
Only stop
${loop} = Check test and get loop ${TEST NAME}
- Should be IN RANGE loop ${loop} 100
- Should be FOR iteration ${loop.body[0]} \${i}=0
- Check log message ${loop.body[0].body[1].msgs[0]} i: 0
- Should be FOR iteration ${loop.body[1]} \${i}=1
- Check log message ${loop.body[1].body[1].msgs[0]} i: 1
- Should be FOR iteration ${loop.body[42]} \${i}=42
- Check log message ${loop.body[42].body[1].msgs[0]} i: 42
- Should be FOR iteration ${loop.body[-1]} \${i}=99
- Check log message ${loop.body[-1].body[1].msgs[0]} i: 99
+ Should be IN RANGE loop ${loop} 100
+ Should be FOR iteration ${loop[0]} \${i}=0
+ Check log message ${loop[0, 1, 0]} i: 0
+ Should be FOR iteration ${loop[1]} \${i}=1
+ Check log message ${loop[1, 1, 0]} i: 1
+ Should be FOR iteration ${loop[42]} \${i}=42
+ Check log message ${loop[42, 1, 0]} i: 42
+ Should be FOR iteration ${loop[-1]} \${i}=99
+ Check log message ${loop[-1, 1, 0]} i: 99
Start and stop
- ${loop} = Check test and get loop ${TEST NAME}
- Should be IN RANGE loop ${loop} 4
+ ${loop} = Check test and get loop ${TEST NAME}
+ Should be IN RANGE loop ${loop} 4
Start, stop and step
- ${loop} = Check test and get loop ${TEST NAME}
- Should be IN RANGE loop ${loop} 3
- Should be FOR iteration ${loop.body[0]} \${item}=10
- Should be FOR iteration ${loop.body[1]} \${item}=7
- Should be FOR iteration ${loop.body[2]} \${item}=4
+ ${loop} = Check test and get loop ${TEST NAME}
+ Should be IN RANGE loop ${loop} 3
+ Should be FOR iteration ${loop[0]} \${item}=10
+ Should be FOR iteration ${loop[1]} \${item}=7
+ Should be FOR iteration ${loop[2]} \${item}=4
Float stop
- ${loop} = Check test and get loop ${TEST NAME} 1
- Should be IN RANGE loop ${loop} 4
- Should be FOR iteration ${loop.body[0]} \${item}=0.0
- Should be FOR iteration ${loop.body[1]} \${item}=1.0
- Should be FOR iteration ${loop.body[2]} \${item}=2.0
- Should be FOR iteration ${loop.body[3]} \${item}=3.0
- ${loop} = Check test and get loop ${TEST NAME} 2
- Should be IN RANGE loop ${loop} 3
- Should be FOR iteration ${loop.body[0]} \${item}=0.0
- Should be FOR iteration ${loop.body[1]} \${item}=1.0
- Should be FOR iteration ${loop.body[2]} \${item}=2.0
+ ${loop} = Check test and get loop ${TEST NAME} 1
+ Should be IN RANGE loop ${loop} 4
+ Should be FOR iteration ${loop[0]} \${item}=0.0
+ Should be FOR iteration ${loop[1]} \${item}=1.0
+ Should be FOR iteration ${loop[2]} \${item}=2.0
+ Should be FOR iteration ${loop[3]} \${item}=3.0
+ ${loop} = Check test and get loop ${TEST NAME} 2
+ Should be IN RANGE loop ${loop} 3
+ Should be FOR iteration ${loop[0]} \${item}=0.0
+ Should be FOR iteration ${loop[1]} \${item}=1.0
+ Should be FOR iteration ${loop[2]} \${item}=2.0
Float start and stop
- ${loop} = Check test and get loop ${TEST NAME} 1
- Should be IN RANGE loop ${loop} 3
- Should be FOR iteration ${loop.body[0]} \${item}=-1.5
- Should be FOR iteration ${loop.body[1]} \${item}=-0.5
- Should be FOR iteration ${loop.body[2]} \${item}=0.5
- ${loop} = Check test and get loop ${TEST NAME} 2 0
- Should be IN RANGE loop ${loop} 4
- Should be FOR iteration ${loop.body[0]} \${item}=-1.5
- Should be FOR iteration ${loop.body[1]} \${item}=-0.5
- Should be FOR iteration ${loop.body[2]} \${item}=0.5
- Should be FOR iteration ${loop.body[3]} \${item}=1.5
+ ${loop} = Check test and get loop ${TEST NAME} 1
+ Should be IN RANGE loop ${loop} 3
+ Should be FOR iteration ${loop[0]} \${item}=-1.5
+ Should be FOR iteration ${loop[1]} \${item}=-0.5
+ Should be FOR iteration ${loop[2]} \${item}=0.5
+ ${loop} = Check test and get loop ${TEST NAME} 2 0
+ Should be IN RANGE loop ${loop} 4
+ Should be FOR iteration ${loop[0]} \${item}=-1.5
+ Should be FOR iteration ${loop[1]} \${item}=-0.5
+ Should be FOR iteration ${loop[2]} \${item}=0.5
+ Should be FOR iteration ${loop[3]} \${item}=1.5
Float start, stop and step
- ${loop} = Check test and get loop ${TEST NAME}
- Should be IN RANGE loop ${loop} 3
- Should be FOR iteration ${loop.body[0]} \${item}=10.99
- Should be FOR iteration ${loop.body[1]} \${item}=7.95
- Should be FOR iteration ${loop.body[2]} \${item}=4.91
+ ${loop} = Check test and get loop ${TEST NAME}
+ Should be IN RANGE loop ${loop} 3
+ Should be FOR iteration ${loop[0]} \${item}=10.99
+ Should be FOR iteration ${loop[1]} \${item}=7.95
+ Should be FOR iteration ${loop[2]} \${item}=4.91
Variables in arguments
- ${loop} = Check test and get loop ${TEST NAME} 0
- Should be IN RANGE loop ${loop} 2
- ${loop} = Check test and get loop ${TEST NAME} 2
- Should be IN RANGE loop ${loop} 1
+ ${loop} = Check test and get loop ${TEST NAME} 0
+ Should be IN RANGE loop ${loop} 2
+ ${loop} = Check test and get loop ${TEST NAME} 2
+ Should be IN RANGE loop ${loop} 1
Calculations
Check test case ${TEST NAME}
@@ -72,15 +72,15 @@ Calculations with floats
Check test case ${TEST NAME}
Multiple variables
- ${loop} = Check test and get loop ${TEST NAME} 0
- Should be IN RANGE loop ${loop} 1
- Should be FOR iteration ${loop.body[0]} \${a}=0 \${b}=1 \${c}=2 \${d}=3 \${e}=4
- ${loop} = Check test and get loop ${TEST NAME} 2
- Should be IN RANGE loop ${loop} 4
- Should be FOR iteration ${loop.body[0]} \${i}=-1 \${j}=0 \${k}=1
- Should be FOR iteration ${loop.body[1]} \${i}=2 \${j}=3 \${k}=4
- Should be FOR iteration ${loop.body[2]} \${i}=5 \${j}=6 \${k}=7
- Should be FOR iteration ${loop.body[3]} \${i}=8 \${j}=9 \${k}=10
+ ${loop} = Check test and get loop ${TEST NAME} 0
+ Should be IN RANGE loop ${loop} 1
+ Should be FOR iteration ${loop[0]} \${a}=0 \${b}=1 \${c}=2 \${d}=3 \${e}=4
+ ${loop} = Check test and get loop ${TEST NAME} 2
+ Should be IN RANGE loop ${loop} 4
+ Should be FOR iteration ${loop[0]} \${i}=-1 \${j}=0 \${k}=1
+ Should be FOR iteration ${loop[1]} \${i}=2 \${j}=3 \${k}=4
+ Should be FOR iteration ${loop[2]} \${i}=5 \${j}=6 \${k}=7
+ Should be FOR iteration ${loop[3]} \${i}=8 \${j}=9 \${k}=10
Too many arguments
Check test and failed loop ${TEST NAME} IN RANGE
diff --git a/atest/robot/running/for/for_in_zip.robot b/atest/robot/running/for/for_in_zip.robot
index 41a9de220ea..987e080ff40 100644
--- a/atest/robot/running/for/for_in_zip.robot
+++ b/atest/robot/running/for/for_in_zip.robot
@@ -5,122 +5,122 @@ Resource for.resource
*** Test Cases ***
Two variables and lists
${loop} = Check test and get loop ${TEST NAME}
- Should be IN ZIP loop ${loop} 3
- Should be FOR iteration ${loop.body[0]} \${x}=a \${y}=x
- Should be FOR iteration ${loop.body[1]} \${x}=b \${y}=y
- Should be FOR iteration ${loop.body[2]} \${x}=c \${y}=z
+ Should be IN ZIP loop ${loop} 3
+ Should be FOR iteration ${loop[0]} \${x}=a \${y}=x
+ Should be FOR iteration ${loop[1]} \${x}=b \${y}=y
+ Should be FOR iteration ${loop[2]} \${x}=c \${y}=z
Uneven lists cause deprecation warning by default
${loop} = Check test and get loop ${TEST NAME}
- Should be IN ZIP loop ${loop} 3
- Check Log Message ${loop.body[0]}
+ Should be IN ZIP loop ${loop} 3
+ Check Log Message ${loop[0]}
... FOR IN ZIP default mode will be changed from SHORTEST to STRICT in Robot Framework 8.0. Use 'mode=SHORTEST' to keep using the SHORTEST mode. If the mode is not changed, execution will fail like this in the future: FOR IN ZIP items must have equal lengths in the STRICT mode, but lengths are 3 and 5. WARN
- Should be FOR iteration ${loop.body[1]} \${x}=a \${y}=1
- Should be FOR iteration ${loop.body[2]} \${x}=b \${y}=2
- Should be FOR iteration ${loop.body[3]} \${x}=c \${y}=3
+ Should be FOR iteration ${loop[1]} \${x}=a \${y}=1
+ Should be FOR iteration ${loop[2]} \${x}=b \${y}=2
+ Should be FOR iteration ${loop[3]} \${x}=c \${y}=3
Three variables and lists
${loop} = Check test and get loop ${TEST NAME}
- Should be IN ZIP loop ${loop} 3
- Should be FOR iteration ${loop.body[0]} \${x}=a \${y}=x \${z}=1
- Should be FOR iteration ${loop.body[1]} \${x}=b \${y}=y \${z}=2
- Should be FOR iteration ${loop.body[2]} \${x}=c \${y}=z \${z}=3
+ Should be IN ZIP loop ${loop} 3
+ Should be FOR iteration ${loop[0]} \${x}=a \${y}=x \${z}=1
+ Should be FOR iteration ${loop[1]} \${x}=b \${y}=y \${z}=2
+ Should be FOR iteration ${loop[2]} \${x}=c \${y}=z \${z}=3
Six variables and lists
${loop} = Check test and get loop ${TEST NAME}
- Should be IN ZIP loop ${loop} 3
- Should be FOR iteration ${loop.body[0]} \${x}=a \${y}=x \${z}=1 \${å}=1 \${ä}=x \${ö}=a
- Should be FOR iteration ${loop.body[1]} \${x}=b \${y}=y \${z}=2 \${å}=2 \${ä}=y \${ö}=b
- Should be FOR iteration ${loop.body[2]} \${x}=c \${y}=z \${z}=3 \${å}=3 \${ä}=z \${ö}=c
+ Should be IN ZIP loop ${loop} 3
+ Should be FOR iteration ${loop[0]} \${x}=a \${y}=x \${z}=1 \${å}=1 \${ä}=x \${ö}=a
+ Should be FOR iteration ${loop[1]} \${x}=b \${y}=y \${z}=2 \${å}=2 \${ä}=y \${ö}=b
+ Should be FOR iteration ${loop[2]} \${x}=c \${y}=z \${z}=3 \${å}=3 \${ä}=z \${ö}=c
One variable and list
${loop} = Check test and get loop ${TEST NAME}
- Should be IN ZIP loop ${loop} 3
- Should be FOR iteration ${loop.body[0]} \${x}=a
- Should be FOR iteration ${loop.body[1]} \${x}=b
- Should be FOR iteration ${loop.body[2]} \${x}=c
+ Should be IN ZIP loop ${loop} 3
+ Should be FOR iteration ${loop[0]} \${x}=a
+ Should be FOR iteration ${loop[1]} \${x}=b
+ Should be FOR iteration ${loop[2]} \${x}=c
One variable and two lists
${loop} = Check test and get loop ${TEST NAME}
- Should be IN ZIP loop ${loop} 3
- Should be FOR iteration ${loop.body[0]} \${x}=('a', 'x')
- Should be FOR iteration ${loop.body[1]} \${x}=('b', 'y')
- Should be FOR iteration ${loop.body[2]} \${x}=('c', 'z')
+ Should be IN ZIP loop ${loop} 3
+ Should be FOR iteration ${loop[0]} \${x}=('a', 'x')
+ Should be FOR iteration ${loop[1]} \${x}=('b', 'y')
+ Should be FOR iteration ${loop[2]} \${x}=('c', 'z')
One variable and six lists
${loop} = Check test and get loop ${TEST NAME}
- Should be IN ZIP loop ${loop} 3
- Should be FOR iteration ${loop.body[0]} \${x}=('a', 'x', 1, 1, 'x', 'a')
- Should be FOR iteration ${loop.body[1]} \${x}=('b', 'y', 2, 2, 'y', 'b')
- Should be FOR iteration ${loop.body[2]} \${x}=('c', 'z', 3, 3, 'z', 'c')
+ Should be IN ZIP loop ${loop} 3
+ Should be FOR iteration ${loop[0]} \${x}=('a', 'x', 1, 1, 'x', 'a')
+ Should be FOR iteration ${loop[1]} \${x}=('b', 'y', 2, 2, 'y', 'b')
+ Should be FOR iteration ${loop[2]} \${x}=('c', 'z', 3, 3, 'z', 'c')
Other iterables
Check Test Case ${TEST NAME}
List variable containing iterables
${loop} = Check test and get loop ${TEST NAME} 1
- Should be IN ZIP loop ${loop} 3
- Should be FOR iteration ${loop.body[0]} \${x}=a \${y}=x \${z}=f
- Should be FOR iteration ${loop.body[1]} \${x}=b \${y}=y \${z}=o
- Should be FOR iteration ${loop.body[2]} \${x}=c \${y}=z \${z}=o
+ Should be IN ZIP loop ${loop} 3
+ Should be FOR iteration ${loop[0]} \${x}=a \${y}=x \${z}=f
+ Should be FOR iteration ${loop[1]} \${x}=b \${y}=y \${z}=o
+ Should be FOR iteration ${loop[2]} \${x}=c \${y}=z \${z}=o
List variable with iterables can be empty
${tc} = Check Test Case ${TEST NAME}
- Should be IN ZIP loop ${tc.body[0]} 1 NOT RUN
- Should be FOR iteration ${tc.body[0].body[0]} \${x}=
- Should be IN ZIP loop ${tc.body[1]} 1 NOT RUN
- Should be FOR iteration ${tc.body[1].body[0]} \${x}= \${y}= \${z}=
- Check Log Message ${tc.body[2].msgs[0]} Executed!
+ Should be IN ZIP loop ${tc[0]} 1 NOT RUN
+ Should be FOR iteration ${tc[0, 0]} \${x}=
+ Should be IN ZIP loop ${tc[1]} 1 NOT RUN
+ Should be FOR iteration ${tc[1, 0]} \${x}= \${y}= \${z}=
+ Check Log Message ${tc[2, 0]} Executed!
Strict mode
${tc} = Check Test Case ${TEST NAME}
- Should be IN ZIP loop ${tc.body[0]} 3 PASS mode=STRICT
- Should be IN ZIP loop ${tc.body[2]} 1 FAIL mode=strict
+ Should be IN ZIP loop ${tc[0]} 3 PASS mode=STRICT
+ Should be IN ZIP loop ${tc[2]} 1 FAIL mode=strict
Strict mode requires items to have length
${tc} = Check Test Case ${TEST NAME}
- Should be IN ZIP loop ${tc.body[0]} 1 FAIL mode=STRICT
+ Should be IN ZIP loop ${tc[0]} 1 FAIL mode=STRICT
Shortest mode
${tc} = Check Test Case ${TEST NAME}
- Should be IN ZIP loop ${tc.body[0]} 3 PASS mode=SHORTEST fill=ignored
- Should be IN ZIP loop ${tc.body[3]} 3 PASS mode=\${{'shortest'}}
+ Should be IN ZIP loop ${tc[0]} 3 PASS mode=SHORTEST fill=ignored
+ Should be IN ZIP loop ${tc[3]} 3 PASS mode=\${{'shortest'}}
Shortest mode supports infinite iterators
${tc} = Check Test Case ${TEST NAME}
- Should be IN ZIP loop ${tc.body[0]} 3 PASS mode=SHORTEST
+ Should be IN ZIP loop ${tc[0]} 3 PASS mode=SHORTEST
Longest mode
${tc} = Check Test Case ${TEST NAME}
- Should be IN ZIP loop ${tc.body[0]} 3 PASS mode=LONGEST
- Should be IN ZIP loop ${tc.body[3]} 5 PASS mode=LoNgEsT
+ Should be IN ZIP loop ${tc[0]} 3 PASS mode=LONGEST
+ Should be IN ZIP loop ${tc[3]} 5 PASS mode=LoNgEsT
Longest mode with custom fill value
${tc} = Check Test Case ${TEST NAME}
- Should be IN ZIP loop ${tc.body[0]} 5 PASS mode=longest fill=?
- Should be IN ZIP loop ${tc.body[3]} 3 PASS mode=longest fill=\${0}
+ Should be IN ZIP loop ${tc[0]} 5 PASS mode=longest fill=?
+ Should be IN ZIP loop ${tc[3]} 3 PASS mode=longest fill=\${0}
Invalid mode
${tc} = Check Test Case ${TEST NAME}
- Should be IN ZIP loop ${tc.body[0]} 1 FAIL mode=bad
+ Should be IN ZIP loop ${tc[0]} 1 FAIL mode=bad
Invalid mode from variable
${tc} = Check Test Case ${TEST NAME}
- Should be IN ZIP loop ${tc.body[0]} 1 FAIL mode=\${{'bad'}}
+ Should be IN ZIP loop ${tc[0]} 1 FAIL mode=\${{'bad'}}
Config more than once
${tc} = Check Test Case ${TEST NAME} 1
- Should be IN ZIP loop ${tc.body[0]} 1 FAIL mode=shortest
+ Should be IN ZIP loop ${tc[0]} 1 FAIL mode=shortest
${tc} = Check Test Case ${TEST NAME} 2
- Should be IN ZIP loop ${tc.body[0]} 1 FAIL fill=z
+ Should be IN ZIP loop ${tc[0]} 1 FAIL fill=z
Non-existing variable in mode
${tc} = Check Test Case ${TEST NAME}
- Should be IN ZIP loop ${tc.body[0]} 1 FAIL mode=\${bad} fill=\${ignored}
+ Should be IN ZIP loop ${tc[0]} 1 FAIL mode=\${bad} fill=\${ignored}
Non-existing variable in fill value
${tc} = Check Test Case ${TEST NAME}
- Should be IN ZIP loop ${tc.body[0]} 1 FAIL mode=longest fill=\${bad}
+ Should be IN ZIP loop ${tc[0]} 1 FAIL mode=longest fill=\${bad}
Not iterable value
Check test and failed loop ${TEST NAME} IN ZIP
diff --git a/atest/robot/running/group/group.robot b/atest/robot/running/group/group.robot
new file mode 100644
index 00000000000..f579f090cf5
--- /dev/null
+++ b/atest/robot/running/group/group.robot
@@ -0,0 +1,42 @@
+*** Settings ***
+Suite Setup Run Tests ${EMPTY} running/group/group.robot
+Resource atest_resource.robot
+
+*** Test Cases ***
+Basics
+ ${tc}= Check Test Case ${TESTNAME}
+ Check Body Item Data ${tc[0]} type=GROUP name=1st group children=2
+ Check Body Item Data ${tc[0, 0]} type=KEYWORD name=Log args=Inside group
+ Check Body Item Data ${tc[0, 1]} type=KEYWORD name=Log args=Still inside
+ Check Body Item Data ${tc[1]} type=GROUP name=second children=1
+ Check Body Item Data ${tc[1, 0]} type=KEYWORD name=Log args=Inside second group
+ Check Body Item Data ${tc[2]} type=KEYWORD name=Log args=After
+
+Failing
+ ${tc}= Check Test Case ${TESTNAME}
+ Check Body Item Data ${tc[0]} type=GROUP name=Fails children=2 status=FAIL message=Failing inside GROUP!
+ Check Body Item Data ${tc[0, 0]} type=KEYWORD name=Fail children=1 status=FAIL message=Failing inside GROUP!
+ Check Body Item Data ${tc[0, 1]} type=KEYWORD name=Fail children=0 status=NOT RUN
+ Check Body Item Data ${tc[1]} type=GROUP name=Not run children=1 status=NOT RUN
+ Check Body Item Data ${tc[1, 0]} type=KEYWORD name=Fail children=0 status=NOT RUN
+
+Anonymous
+ ${tc}= Check Test Case ${TESTNAME}
+ Check Body Item Data ${tc[0]} type=GROUP name=${EMPTY} children=1
+ Check Body Item Data ${tc[0, 0]} type=KEYWORD name=Log args=Inside unnamed group
+
+Variable in name
+ ${tc}= Check Test Case ${TESTNAME}
+ Check Body Item Data ${tc[0]} type=GROUP name=Test is named: ${TEST NAME} children=1
+ Check Body Item Data ${tc[0, 0]} type=KEYWORD name=Log args=\${TEST_NAME}
+ Check Log Message ${tc[0, 0, 0]} ${TEST NAME}
+ Check Body Item Data ${tc[1]} type=GROUP name=42 children=1
+ Check Body Item Data ${tc[1, 0]} type=KEYWORD name=Log args=Should be 42
+
+In user keyword
+ ${tc}= Check Test Case ${TESTNAME}
+ Check Body Item Data ${tc[0]} type=KEYWORD name=Keyword children=4
+ Check Body Item Data ${tc[0, 0]} type=KEYWORD name=Log args=Before
+ Check Body Item Data ${tc[0, 1]} type=GROUP name=First children=2
+ Check Body Item Data ${tc[0, 2]} type=GROUP name=Second children=1
+ Check Body Item Data ${tc[0, 3]} type=KEYWORD name=Log args=After
diff --git a/atest/robot/running/group/invalid_group.robot b/atest/robot/running/group/invalid_group.robot
new file mode 100644
index 00000000000..f6d415cdaf9
--- /dev/null
+++ b/atest/robot/running/group/invalid_group.robot
@@ -0,0 +1,44 @@
+*** Settings ***
+Suite Setup Run Tests ${EMPTY} running/group/invalid_group.robot
+Resource atest_resource.robot
+
+*** Test Cases ***
+END missing
+ ${tc} = Check Test Case ${TESTNAME}
+ Length Should Be ${tc.body} 1
+ Check Body Item Data ${tc[0]} GROUP status=FAIL children=2 message=GROUP must have closing END.
+ Check Body Item Data ${tc[0, 0]} KEYWORD status=NOT RUN children=0 name=Fail args=Not run
+ Check Body Item Data ${tc[0, 1]} MESSAGE level=FAIL message=GROUP must have closing END.
+
+Empty
+ ${tc} Check Test Case ${TESTNAME}
+ Length Should Be ${tc.body} 2
+ Check Body Item Data ${tc[0]} GROUP status=FAIL children=1 message=GROUP cannot be empty.
+ Check Body Item Data ${tc[0, 0]} MESSAGE level=FAIL message=GROUP cannot be empty.
+ Check Body Item Data ${tc[1]} KEYWORD status=NOT RUN children=0 name=Log args=Outside
+
+Multiple parameters
+ ${tc} Check Test Case ${TESTNAME}
+ Length Should Be ${tc.body} 2
+ Check Body Item Data ${tc[0]} GROUP status=FAIL children=2 message=GROUP accepts only one argument as name, got 3 arguments 'Too', 'many' and 'values'.
+ Check Body Item Data ${tc[0, 0]} KEYWORD status=NOT RUN children=0 name=Fail args=Not run
+ Check Body Item Data ${tc[0, 1]} MESSAGE level=FAIL message=GROUP accepts only one argument as name, got 3 arguments 'Too', 'many' and 'values'.
+ Check Body Item Data ${tc[1]} KEYWORD status=NOT RUN children=0 name=Log args=Last Keyword
+
+Non-existing variable in name
+ ${tc} Check Test Case ${TESTNAME}
+ Length Should Be ${tc.body} 2
+ Check Body Item Data ${tc[0]} GROUP status=FAIL children=2 message=Variable '\${non_existing_var}' not found. name=\${non_existing_var} in name
+ Check Body Item Data ${tc[0, 0]} KEYWORD status=NOT RUN children=0 name=Fail args=Not run
+ Check Body Item Data ${tc[0, 1]} MESSAGE level=FAIL message=Variable '\${non_existing_var}' not found.
+ Check Body Item Data ${tc[1]} KEYWORD status=NOT RUN children=0 name=Log args=Last Keyword
+
+Invalid data is not reported after failures
+ ${tc} Check Test Case ${TESTNAME}
+ Length Should Be ${tc.body} 4
+ Check Body Item Data ${tc[0]} KEYWORD status=FAIL children=1 name=Fail args=Something bad happened! message=Something bad happened!
+ Check Body Item Data ${tc[1]} GROUP status=NOT RUN children=1 name=\${non_existing_non_executed_variable_is_ok}
+ Check Body Item Data ${tc[1, 0]} KEYWORD status=NOT RUN children=0 name=Fail args=Not run
+ Check Body Item Data ${tc[2]} GROUP status=NOT RUN children=0 name=Empty non-executed GROUP is ok
+ Check Body Item Data ${tc[3]} GROUP status=NOT RUN children=1 name=Even missing END is ok
+ Check Body Item Data ${tc[3, 0]} KEYWORD status=NOT RUN children=0 name=Fail args=Not run
diff --git a/atest/robot/running/group/nesting_group.robot b/atest/robot/running/group/nesting_group.robot
new file mode 100644
index 00000000000..1d612e0c189
--- /dev/null
+++ b/atest/robot/running/group/nesting_group.robot
@@ -0,0 +1,51 @@
+*** Settings ***
+Suite Setup Run Tests ${EMPTY} running/group/nesting_group.robot
+Resource atest_resource.robot
+
+*** Test Cases ***
+Nested
+ ${tc} Check Test Case ${TESTNAME}
+ Check Body Item Data ${tc[0]} type=GROUP name=
+ Check Body Item Data ${tc[0, 0]} type=KEYWORD name=Set Variable
+ Check Body Item Data ${tc[0, 1]} type=GROUP name=This Is A Named Group
+ Check Body Item Data ${tc[0, 1, 0]} type=KEYWORD name=Should Be Equal
+
+With other control structures
+ ${tc} Check Test Case ${TESTNAME}
+ Check Body Item Data ${tc[0]} type=IF/ELSE ROOT
+ Check Body Item Data ${tc[0, 0]} type=IF condition=True children=2
+ Check Body Item Data ${tc[0, 0, 0]} type=GROUP name=Hello children=1
+ Check Body Item Data ${tc[0, 0, 0, 0]} type=VAR name=\${i}
+ Check Body Item Data ${tc[0, 0, 1]} type=GROUP name=With WHILE children=2
+ Check Body Item Data ${tc[0, 0, 1, 0]} type=WHILE condition=$i < 2 children=2
+ Check Body Item Data ${tc[0, 0, 1, 0, 0]} type=ITERATION
+ Check Body Item Data ${tc[0, 0, 1, 0, 0, 0]} type=GROUP name=Group1 Inside WHILE (0) children=1
+ Check Body Item Data ${tc[0, 0, 1, 0, 0, 0, 0]} type=KEYWORD name=Log args=\${i}
+ Check Body Item Data ${tc[0, 0, 1, 0, 0, 1]} type=GROUP name=Group2 Inside WHILE children=1
+ Check Body Item Data ${tc[0, 0, 1, 0, 0, 1, 0]} type=VAR name=\${i} value=\${i + 1}
+ Check Body Item Data ${tc[0, 0, 1, 0, 1]} type=ITERATION
+ Check Body Item Data ${tc[0, 0, 1, 0, 1, 0]} type=GROUP name=Group1 Inside WHILE (1) children=1
+ Check Body Item Data ${tc[0, 0, 1, 0, 1, 0, 0]} type=KEYWORD name=Log args=\${i}
+ Check Body Item Data ${tc[0, 0, 1, 0, 1, 1]} type=GROUP name=Group2 Inside WHILE children=1
+ Check Body Item Data ${tc[0, 0, 1, 0, 1, 1, 0]} type=VAR name=\${i} value=\${i + 1}
+ Check Body Item Data ${tc[0, 0, 1, 1]} type=IF/ELSE ROOT
+ Check Body Item Data ${tc[0, 0, 1, 1, 0]} type=IF status=NOT RUN condition=$i != 2 children=1
+ Check Body Item Data ${tc[0, 0, 1, 1, 0, 0]} type=KEYWORD status=NOT RUN name=Fail args=Shall be logged but NOT RUN
+
+In non-executed branch
+ ${tc} Check Test Case ${TESTNAME}
+ Check Body Item Data ${tc[0]} type=VAR name=\${var} value=value
+ Check Body Item Data ${tc[1]} type=IF/ELSE ROOT
+ Check Body Item Data ${tc[1, 0]} type=IF condition=True children=1
+ Check Body Item Data ${tc[1, 0, 0]} type=GROUP name=GROUP in IF children=2
+ Check Body Item Data ${tc[1, 0, 0, 0]} type=KEYWORD name=Should Be Equal
+ Check Body Item Data ${tc[1, 0, 0, 1]} type=IF/ELSE ROOT
+ Check Body Item Data ${tc[1, 0, 0, 1, 0]} type=IF status=PASS children=1 condition=True
+ Check Body Item Data ${tc[1, 0, 0, 1, 0, 0]} type=KEYWORD status=PASS name=Log args=IF in GROUP
+ Check Body Item Data ${tc[1, 0, 0, 1, 1]} type=ELSE status=NOT RUN
+ Check Body Item Data ${tc[1, 0, 0, 1, 1, 0]} type=GROUP status=NOT RUN children=1 name=GROUP in ELSE
+ Check Body Item Data ${tc[1, 0, 0, 1, 1, 0, 0]} type=KEYWORD status=NOT RUN name=Fail args=Shall be logged but NOT RUN
+ Check Body Item Data ${tc[1, 1]} type=ELSE IF status=NOT RUN
+ Check Body Item Data ${tc[1, 1, 0]} type=GROUP status=NOT RUN children=1 name=\${non_existing_variable_is_fine_here}
+ Check Body Item Data ${tc[1, 2]} type=ELSE status=NOT RUN
+ Check Body Item Data ${tc[1, 2, 0]} type=GROUP status=NOT RUN children=0 name=Even empty GROUP is allowed
diff --git a/atest/robot/running/group/templates.robot b/atest/robot/running/group/templates.robot
new file mode 100644
index 00000000000..b42966b2524
--- /dev/null
+++ b/atest/robot/running/group/templates.robot
@@ -0,0 +1,69 @@
+*** Settings ***
+Suite Setup Run Tests ${EMPTY} running/group/templates.robot
+Resource atest_resource.robot
+
+*** Test Cases ***
+Pass
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Body Item Data ${tc[0]} type=GROUP status=PASS children=1 name=1
+ Check Body Item Data ${tc[0, 0]} type=KEYWORD status=PASS children=1 name=Run Keyword args=Log, 1.1
+ Check Body Item Data ${tc[1]} type=GROUP status=PASS children=2 name=2
+ Check Body Item Data ${tc[1, 0]} type=KEYWORD status=PASS children=1 name=Run Keyword args=Log, 2.1
+ Check Body Item Data ${tc[1, 1]} type=KEYWORD status=PASS children=1 name=Run Keyword args=Log, 2.2
+
+Pass and fail
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Body Item Data ${tc[0]} type=GROUP status=PASS children=1 name=1
+ Check Body Item Data ${tc[0, 0]} type=KEYWORD status=PASS children=1 name=Run Keyword args=Log, 1.1
+ Check Body Item Data ${tc[1]} type=GROUP status=FAIL children=2 name=2 message=2.1
+ Check Body Item Data ${tc[1, 0]} type=KEYWORD status=FAIL children=1 name=Run Keyword args=Fail, 2.1 message=2.1
+ Check Body Item Data ${tc[1, 1]} type=KEYWORD status=PASS children=1 name=Run Keyword args=Log, 2.2
+ Check Body Item Data ${tc[2]} type=GROUP status=PASS children=1 name=3
+ Check Body Item Data ${tc[2, 0]} type=KEYWORD status=PASS children=1 name=Run Keyword args=Log, 3.1
+
+Fail multiple times
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Body Item Data ${tc[0]} type=GROUP status=FAIL children=1 name=1 message=1.1
+ Check Body Item Data ${tc[0, 0]} type=KEYWORD status=FAIL children=1 name=Run Keyword args=Fail, 1.1 message=1.1
+ Check Body Item Data ${tc[1]} type=GROUP status=FAIL children=3 name=2 message=Several failures occurred:\n\n1) 2.1\n\n2) 2.3
+ Check Body Item Data ${tc[1, 0]} type=KEYWORD status=FAIL children=1 name=Run Keyword args=Fail, 2.1 message=2.1
+ Check Body Item Data ${tc[1, 1]} type=KEYWORD status=PASS children=1 name=Run Keyword args=Log, 2.2
+ Check Body Item Data ${tc[1, 2]} type=KEYWORD status=FAIL children=1 name=Run Keyword args=Fail, 2.3 message=2.3
+ Check Body Item Data ${tc[2]} type=GROUP status=PASS children=1 name=3
+ Check Body Item Data ${tc[2, 0]} type=KEYWORD status=PASS children=1 name=Run Keyword args=Log, 3.1
+ Check Body Item Data ${tc[3]} type=GROUP status=FAIL children=1 name=4 message=4.1
+ Check Body Item Data ${tc[3, 0]} type=KEYWORD status=FAIL children=1 name=Run Keyword args=Fail, 4.1 message=4.1
+
+Pass and skip
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Body Item Data ${tc[0]} type=GROUP status=SKIP children=1 name=1 message=1.1
+ Check Body Item Data ${tc[0, 0]} type=KEYWORD status=SKIP children=1 name=Run Keyword args=Skip, 1.1 message=1.1
+ Check Body Item Data ${tc[1]} type=GROUP status=PASS children=1 name=2
+ Check Body Item Data ${tc[1, 0]} type=KEYWORD status=PASS children=1 name=Run Keyword args=Log, 2.1
+ Check Body Item Data ${tc[2]} type=GROUP status=PASS children=2 name=3
+ Check Body Item Data ${tc[2, 0]} type=KEYWORD status=SKIP children=1 name=Run Keyword args=Skip, 3.1 message=3.1
+ Check Body Item Data ${tc[2, 1]} type=KEYWORD status=PASS children=1 name=Run Keyword args=Log, 3.2
+
+Pass, fail and skip
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Body Item Data ${tc[0]} type=GROUP status=FAIL children=3 name=1 message=1.1
+ Check Body Item Data ${tc[0, 0]} type=KEYWORD status=FAIL children=1 name=Run Keyword args=Fail, 1.1 message=1.1
+ Check Body Item Data ${tc[0, 1]} type=KEYWORD status=SKIP children=1 name=Run Keyword args=Skip, 1.2 message=1.2
+ Check Body Item Data ${tc[0, 2]} type=KEYWORD status=PASS children=1 name=Run Keyword args=Log, 1.3
+ Check Body Item Data ${tc[1]} type=GROUP status=SKIP children=1 name=2 message=2.1
+ Check Body Item Data ${tc[1, 0]} type=KEYWORD status=SKIP children=1 name=Run Keyword args=Skip, 2.1 message=2.1
+ Check Body Item Data ${tc[2]} type=GROUP status=PASS children=1 name=3
+ Check Body Item Data ${tc[2, 0]} type=KEYWORD status=PASS children=1 name=Run Keyword args=Log, 3.1
+
+Skip all
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Body Item Data ${tc[0]} type=GROUP status=SKIP children=2 name=1 message=All iterations skipped.
+ Check Body Item Data ${tc[0, 0]} type=KEYWORD status=SKIP children=1 name=Run Keyword args=Skip, 1.1 message=1.1
+ Check Body Item Data ${tc[0, 1]} type=KEYWORD status=SKIP children=1 name=Run Keyword args=Skip, 1.2 message=1.2
+ Check Body Item Data ${tc[1]} type=GROUP status=SKIP children=1 name=2 message=2.1
+ Check Body Item Data ${tc[1, 0]} type=KEYWORD status=SKIP children=1 name=Run Keyword args=Skip, 2.1 message=2.1
+
+Just one that is skipped
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Body Item Data ${tc[0]} type=GROUP status=SKIP children=1 name=1 message=1.1
+ Check Body Item Data ${tc[0, 0]} type=KEYWORD status=SKIP children=1 name=Run Keyword args=Skip, 1.1 message=1.1
diff --git a/atest/robot/running/html_error_message.robot b/atest/robot/running/html_error_message.robot
index 007d5a3cd04..9ff087a0d82 100644
--- a/atest/robot/running/html_error_message.robot
+++ b/atest/robot/running/html_error_message.robot
@@ -9,15 +9,15 @@ ${FAILURE} Robot Framework
*** Test Cases ***
Set Test Message
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} Set test message to:\n${MESSAGE} html=True
+ Check Log Message ${tc[0, 0]} Set test message to:\n${MESSAGE} html=True
HTML failure
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} ${FAILURE} FAIL html=True
+ Check Log Message ${tc[0, 0]} ${FAILURE} FAIL html=True
HTML failure with non-generic exception
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} ValueError: Invalid value FAIL html=True
+ Check Log Message ${tc[0, 0]} ValueError: Invalid value FAIL html=True
HTML failure in setup
Check Test Case ${TESTNAME}
@@ -30,8 +30,8 @@ Normal failure in body and HTML failure in teardown
HTML failure in body and normal failure teardown
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} Should be HTML FAIL html=True
- Check Log Message ${tc.teardown.msgs[0]} Should NOT be HTML FAIL html=False
+ Check Log Message ${tc[0, 0]} Should be HTML FAIL html=True
+ Check Log Message ${tc.teardown[0]} Should NOT be HTML FAIL html=False
HTML failure in body and in teardown
Check Test Case ${TESTNAME}
diff --git a/atest/robot/running/if/complex_if.robot b/atest/robot/running/if/complex_if.robot
index 49afc29aaca..1e4662b1f12 100644
--- a/atest/robot/running/if/complex_if.robot
+++ b/atest/robot/running/if/complex_if.robot
@@ -14,7 +14,7 @@ If inside for loop
Setting after if
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.teardown.msgs[0]} Teardown was found and executed.
+ Check Log Message ${tc.teardown[0]} Teardown was found and executed.
For loop inside if
Check Test Case ${TESTNAME}
@@ -24,23 +24,23 @@ For loop inside for loop
Direct Boolean condition
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.body[0].status} PASS
- Should Be Equal ${tc.body[0].body[0].status} PASS
- Should Be Equal ${tc.body[0].body[0].body[0].status} PASS
+ Should Be Equal ${tc[0].status} PASS
+ Should Be Equal ${tc[0, 0].status} PASS
+ Should Be Equal ${tc[0, 0, 0].status} PASS
Direct Boolean condition false
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.kws[0].status} PASS
- Should Be Equal ${tc.body[0].body[0].status} NOT RUN
- Should Be Equal ${tc.body[0].body[0].body[0].status} NOT RUN
+ Should Be Equal ${tc[0].status} PASS
+ Should Be Equal ${tc[0, 0].status} NOT RUN
+ Should Be Equal ${tc[0, 0, 0].status} NOT RUN
Nesting insanity
Check Test Case ${TESTNAME}
Recursive If
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.kws[0].kws[0].status} PASS
- Should Be Equal ${tc.kws[0].kws[0].kws[0].kws[0].status} PASS
+ Should Be Equal ${tc[0, 0].status} PASS
+ Should Be Equal ${tc[0, 0, 0, 0].status} PASS
If creating variable
Check Test Case ${TESTNAME}
diff --git a/atest/robot/running/if/if_else.robot b/atest/robot/running/if/if_else.robot
index c1d5689808d..6331aeb4dfc 100644
--- a/atest/robot/running/if/if_else.robot
+++ b/atest/robot/running/if/if_else.robot
@@ -41,7 +41,7 @@ If failing in else keyword
Expression evaluation time is included in elapsed time
${tc} = Check Test Case ${TESTNAME}
- Elapsed Time Should Be Valid ${tc.body[0].elapsed_time} minimum=0.2
- Elapsed Time Should Be Valid ${tc.body[0].body[0].elapsed_time} minimum=0.1
- Elapsed Time Should Be Valid ${tc.body[0].body[1].elapsed_time} minimum=0.1
- Elapsed Time Should Be Valid ${tc.body[0].body[2].elapsed_time} maximum=1.0
+ Elapsed Time Should Be Valid ${tc[0].elapsed_time} minimum=0.2
+ Elapsed Time Should Be Valid ${tc[0, 0].elapsed_time} minimum=0.1
+ Elapsed Time Should Be Valid ${tc[0, 1].elapsed_time} minimum=0.1
+ Elapsed Time Should Be Valid ${tc[0, 2].elapsed_time} maximum=1.0
diff --git a/atest/robot/running/if/inline_if_else.robot b/atest/robot/running/if/inline_if_else.robot
index a1418d70100..d3a5a346db5 100644
--- a/atest/robot/running/if/inline_if_else.robot
+++ b/atest/robot/running/if/inline_if_else.robot
@@ -22,10 +22,10 @@ Not executed after failure
Not executed after failure with assignment
[Template] NONE
${tc} = Check Test Case ${TEST NAME}
- Check IF/ELSE Status NOT RUN NOT RUN root=${tc.body[1]} run=False
- Check IF/ELSE Status NOT RUN NOT RUN root=${tc.body[2]} run=False
- Check Keyword Data ${tc.body[1].body[0].body[0]} Not run assign=\${x} status=NOT RUN
- Check Keyword Data ${tc.body[2].body[0].body[0]} Not run assign=\${x}, \@{y} status=NOT RUN
+ Check IF/ELSE Status NOT RUN NOT RUN root=${tc[1]} run=False
+ Check IF/ELSE Status NOT RUN NOT RUN root=${tc[2]} run=False
+ Check Keyword Data ${tc[1, 0, 0]} Not run assign=\${x} status=NOT RUN
+ Check Keyword Data ${tc[2, 0, 0]} Not run assign=\${x}, \@{y} status=NOT RUN
ELSE IF not executed
NOT RUN NOT RUN PASS index=0
@@ -79,20 +79,20 @@ Assign when no branch is run
Inside FOR
[Template] NONE
${tc} = Check Test Case ${TEST NAME}
- Check IF/ELSE Status NOT RUN PASS root=${tc.body[0].body[0].body[0]}
- Check IF/ELSE Status NOT RUN PASS root=${tc.body[0].body[1].body[0]}
- Check IF/ELSE Status FAIL NOT RUN root=${tc.body[0].body[2].body[0]}
+ Check IF/ELSE Status NOT RUN PASS root=${tc[0, 0, 0]}
+ Check IF/ELSE Status NOT RUN PASS root=${tc[0, 1, 0]}
+ Check IF/ELSE Status FAIL NOT RUN root=${tc[0, 2, 0]}
Inside normal IF
[Template] NONE
${tc} = Check Test Case ${TEST NAME}
- Check IF/ELSE Status NOT RUN PASS root=${tc.body[0].body[0].body[1]}
- Check IF/ELSE Status NOT RUN NOT RUN root=${tc.body[0].body[1].body[0]} run=False
+ Check IF/ELSE Status NOT RUN PASS root=${tc[0, 0, 1]}
+ Check IF/ELSE Status NOT RUN NOT RUN root=${tc[0, 1, 0]} run=False
In keyword
[Template] NONE
${tc} = Check Test Case ${TEST NAME}
- Check IF/ELSE Status PASS NOT RUN root=${tc.body[0].body[0]}
- Check IF/ELSE Status NOT RUN PASS NOT RUN root=${tc.body[0].body[1]}
+ Check IF/ELSE Status PASS NOT RUN root=${tc[0, 0]}
+ Check IF/ELSE Status NOT RUN PASS NOT RUN root=${tc[0, 1]}
Check IF/ELSE Status NOT RUN NOT RUN NOT RUN FAIL
- ... NOT RUN NOT RUN NOT RUN root=${tc.body[0].body[2]}
+ ... NOT RUN NOT RUN NOT RUN root=${tc[0, 2]}
diff --git a/atest/robot/running/if/invalid_if.robot b/atest/robot/running/if/invalid_if.robot
index 4f52de27720..714a7b4eb80 100644
--- a/atest/robot/running/if/invalid_if.robot
+++ b/atest/robot/running/if/invalid_if.robot
@@ -28,6 +28,12 @@ ELSE IF with invalid condition
Recommend $var syntax if invalid condition contains ${var}
FAIL index=1
+$var recommendation with multiple variables
+ FAIL index=1
+
+Remove quotes around variable in $var recommendation
+ FAIL index=2
+
IF without END
FAIL
@@ -44,7 +50,7 @@ ELSE IF without condition
ELSE IF with multiple conditions
[Template] NONE
${tc} = Branch statuses should be FAIL NOT RUN NOT RUN
- Should Be Equal ${tc.body[0].body[1].condition} \${False}, ooops, \${True}
+ Should Be Equal ${tc[0, 1].condition} \${False}, ooops, \${True}
ELSE with condition
FAIL NOT RUN
diff --git a/atest/robot/running/if/invalid_inline_if.robot b/atest/robot/running/if/invalid_inline_if.robot
index 01ea3768dd0..5a7ba2c0ac0 100644
--- a/atest/robot/running/if/invalid_inline_if.robot
+++ b/atest/robot/running/if/invalid_inline_if.robot
@@ -65,11 +65,11 @@ Unnecessary END
Invalid END after inline header
[Template] NONE
${tc} = Check Test Case ${TEST NAME}
- Check IF/ELSE Status PASS root=${tc.body[0]}
- Check Log Message ${tc.body[0].body[0].body[0].body[0]} Executed inside inline IF
- Check Log Message ${tc.body[1].body[0]} Executed outside IF
- Should Be Equal ${tc.body[2].type} ERROR
- Should Be Equal ${tc.body[2].status} FAIL
+ Check IF/ELSE Status PASS root=${tc[0]}
+ Check Log Message ${tc[0, 0, 0, 0]} Executed inside inline IF
+ Check Log Message ${tc[1, 0]} Executed outside IF
+ Should Be Equal ${tc[2].type} ERROR
+ Should Be Equal ${tc[2].status} FAIL
Assign in IF branch
FAIL
@@ -107,9 +107,9 @@ Assign when ELSE branch is empty
Control structures are allowed
[Template] NONE
${tc} = Check Test Case ${TESTNAME}
- Check IF/ELSE Status NOT RUN PASS root=${tc.body[0].body[0]}
+ Check IF/ELSE Status NOT RUN PASS root=${tc[0, 0]}
Control structures are not allowed with assignment
[Template] NONE
${tc} = Check Test Case ${TESTNAME}
- Check IF/ELSE Status FAIL NOT RUN root=${tc.body[0].body[0]}
+ Check IF/ELSE Status FAIL NOT RUN root=${tc[0, 0]}
diff --git a/atest/robot/running/invalid_break_and_continue.robot b/atest/robot/running/invalid_break_and_continue.robot
index 6aeafe8493d..6730a116b6a 100644
--- a/atest/robot/running/invalid_break_and_continue.robot
+++ b/atest/robot/running/invalid_break_and_continue.robot
@@ -26,11 +26,11 @@ CONTINUE in TRY-ELSE
CONTINUE with argument in FOR
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.body[0].body[0].body[1].body[0]} CONTINUE does not accept arguments, got 'should not work'. FAIL
+ Check Log Message ${tc[0, 0, 1, 0]} CONTINUE does not accept arguments, got 'should not work'. FAIL
CONTINUE with argument in WHILE
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.body[0].body[0].body[1].body[0]} CONTINUE does not accept arguments, got 'should', 'not' and 'work'. FAIL
+ Check Log Message ${tc[0, 0, 1, 0]} CONTINUE does not accept arguments, got 'should', 'not' and 'work'. FAIL
BREAK in test case
Check Test Case ${TESTNAME}
@@ -55,8 +55,8 @@ BREAK in TRY-ELSE
BREAK with argument in FOR
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.body[0].body[0].body[1].body[0]} BREAK does not accept arguments, got 'should not work'. FAIL
+ Check Log Message ${tc[0, 0, 1, 0]} BREAK does not accept arguments, got 'should not work'. FAIL
BREAK with argument in WHILE
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.body[0].body[0].body[1].body[0]} BREAK does not accept arguments, got 'should', 'not' and 'work'. FAIL
+ Check Log Message ${tc[0, 0, 1, 0]} BREAK does not accept arguments, got 'should', 'not' and 'work'. FAIL
diff --git a/atest/robot/running/long_error_messages.robot b/atest/robot/running/long_error_messages.robot
index 891d106ee07..abef7c57b16 100644
--- a/atest/robot/running/long_error_messages.robot
+++ b/atest/robot/running/long_error_messages.robot
@@ -42,21 +42,21 @@ Has Been Cut
Should Contain ${test.message} ${EXPLANATION}
Should Match Non Empty Regexp ${test.message} ${eol_dots}
Should Match Non Empty Regexp ${test.message} ${bol_dots}
- Error Message In Log Should Not Have Been Cut ${test.kws}
+ Error Message In Log Should Not Have Been Cut ${test}
RETURN ${test}
Error Message In Log Should Not Have Been Cut
- [Arguments] ${kws}
- @{keywords} = Set Variable ${kws}
- FOR ${kw} IN @{keywords}
- Run Keyword If ${kw.msgs}
- ... Should Not Contain ${kw.msgs[-1].message} ${EXPLANATION}
- Error Message In Log Should Not Have Been Cut ${kw.kws}
+ [Arguments] ${item}
+ FOR ${child} IN @{item.non_messages}
+ FOR ${msg} IN @{child.messages}
+ Should Not Contain ${msg.message} ${EXPLANATION}
+ END
+ Error Message In Log Should Not Have Been Cut ${child}
END
Should Match Non Empty Regexp
[Arguments] ${message} ${pattern}
- Run Keyword If $pattern
+ IF $pattern
... Should Match Regexp ${message} ${pattern}
Has Not Been Cut
diff --git a/atest/robot/running/non_ascii_bytes.robot b/atest/robot/running/non_ascii_bytes.robot
index 18b726cd5c2..b84792d1a6e 100644
--- a/atest/robot/running/non_ascii_bytes.robot
+++ b/atest/robot/running/non_ascii_bytes.robot
@@ -8,26 +8,26 @@ Variables ${DATADIR}/running/expbytevalues.py
*** Test Cases ***
In Message
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} ${exp_log_msg}
+ Check Log Message ${tc[0, 0]} ${exp_log_msg}
In Multiline Message
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} ${exp_log_multiline_msg}
+ Check Log Message ${tc[0, 0]} ${exp_log_multiline_msg}
In Return Value
[Documentation] Return value is not altered by the framework and thus it
... contains the exact same bytes that the keyword returned.
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} \${retval} = ${exp_return_msg}
+ Check Log Message ${tc[0, 0]} \${retval} = ${exp_return_msg}
In Exception
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} ${exp_error_msg} FAIL
+ Check Log Message ${tc[0, 0]} ${exp_error_msg} FAIL
In Exception In Setup
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.setup.msgs[0]} ${exp_error_msg} FAIL
+ Check Log Message ${tc.setup[0]} ${exp_error_msg} FAIL
In Exception In Teardown
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.teardown.msgs[0]} ${exp_error_msg} FAIL
+ Check Log Message ${tc.teardown[0]} ${exp_error_msg} FAIL
diff --git a/atest/robot/running/pass_execution.robot b/atest/robot/running/pass_execution.robot
index 2fa03944982..4bea02d7167 100644
--- a/atest/robot/running/pass_execution.robot
+++ b/atest/robot/running/pass_execution.robot
@@ -1,9 +1,9 @@
*** Settings ***
-Suite Setup Run Tests ${EMPTY} running/pass_execution.robot
-Resource atest_resource.robot
+Suite Setup Run Tests ${EMPTY} running/pass_execution.robot
+Resource atest_resource.robot
*** Variables ***
-${PREFIX}= Execution passed with message:\n
+${PREFIX}= Execution passed with message:\n
*** Test Cases ***
Message is required
@@ -11,11 +11,11 @@ Message is required
With message
${tc}= Check Test Tags ${TESTNAME} force1 force2
- Check Log Message ${tc.kws[0].msgs[0]} ${PREFIX}My message
+ Check Log Message ${tc[0, 0]} ${PREFIX}My message
With HTML message
${tc}= Check Test Tags ${TESTNAME} force1 force2
- Check Log Message ${tc.kws[0].msgs[0]} ${PREFIX}Message HTML
+ Check Log Message ${tc[0, 0]} ${PREFIX}Message HTML
Empty message is not allowed
Check Test Case ${TESTNAME}
@@ -40,17 +40,17 @@ Used in template keyword
Used in for loop
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].kws[0].msgs[0]} ${PREFIX}Message with 'foo'
+ Check Log Message ${tc[0, 0, 0, 0]} ${PREFIX}Message with 'foo'
Used in setup
${tc} = Check Test Case ${TESTNAME}
- Keyword Should Have Been Executed ${tc.kws[0]}
+ Keyword Should Have Been Executed ${tc[0]}
Keyword Should Have Been Executed ${tc.teardown}
Used in teardown
${tc}= Check Test Case ${TESTNAME}
Should Be Equal ${tc.teardown.status} PASS
- Check Log Message ${tc.teardown.kws[0].msgs[0]} ${PREFIX}This message is used.
+ Check Log Message ${tc.teardown[0, 0]} ${PREFIX}This message is used.
Before failing teardown
Check Test Case ${TESTNAME}
@@ -60,14 +60,14 @@ After continuable failure
After continuable failure in user keyword
${tc}= Check Test Case ${TESTNAME}
- Should Be Equal ${tc.kws[0].status} FAIL
+ Should Be Equal ${tc[0].status} FAIL
After continuable failure in FOR loop
${tc}= Check Test Case ${TESTNAME}
- Should Be Equal ${tc.kws[0].status} FAIL
- Should Be Equal ${tc.kws[0].kws[0].status} FAIL
- Should Be Equal ${tc.kws[0].kws[0].kws[0].status} FAIL
- Should Be Equal ${tc.kws[0].kws[0].kws[1].status} PASS
+ Should Be Equal ${tc[0].status} FAIL
+ Should Be Equal ${tc[0, 0].status} FAIL
+ Should Be Equal ${tc[0, 0, 0].status} FAIL
+ Should Be Equal ${tc[0, 0, 1].status} PASS
After continuable failure and before failing teardown
Check Test Case ${TESTNAME}
@@ -86,57 +86,57 @@ After continuable failure in keyword teardown
Remove one tag
${tc}= Check Test Tags ${TESTNAME} force2
- Check Log Message ${tc.kws[0].msgs[0]} Removed tag 'force1'.
- Check Log Message ${tc.kws[0].msgs[1]} ${PREFIX}Message
+ Check Log Message ${tc[0, 0]} Removed tag 'force1'.
+ Check Log Message ${tc[0, 1]} ${PREFIX}Message
Remove multiple tags
${tc}= Check Test Tags ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} Removed tags 'force1' and 'force2'.
- Check Log Message ${tc.kws[0].msgs[1]} ${PREFIX}Message
+ Check Log Message ${tc[0, 0]} Removed tags 'force1' and 'force2'.
+ Check Log Message ${tc[0, 1]} ${PREFIX}Message
Remove tags with pattern
${tc}= Check Test Tags ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} Removed tag 'force?'.
- Check Log Message ${tc.kws[0].msgs[1]} ${PREFIX}Message
+ Check Log Message ${tc[0, 0]} Removed tag 'force?'.
+ Check Log Message ${tc[0, 1]} ${PREFIX}Message
Set one tag
${tc}= Check Test Tags ${TESTNAME} force1 force2 tag
- Check Log Message ${tc.kws[0].msgs[0]} Set tag 'tag'.
- Check Log Message ${tc.kws[0].msgs[1]} ${PREFIX}Message
+ Check Log Message ${tc[0, 0]} Set tag 'tag'.
+ Check Log Message ${tc[0, 1]} ${PREFIX}Message
Set multiple tags
${tc}= Check Test Tags ${TESTNAME} force1 force2 tag1 tag2
- Check Log Message ${tc.kws[0].msgs[0]} Set tags 'tag1' and 'tag2'.
- Check Log Message ${tc.kws[0].msgs[1]} ${PREFIX}Message
+ Check Log Message ${tc[0, 0]} Set tags 'tag1' and 'tag2'.
+ Check Log Message ${tc[0, 1]} ${PREFIX}Message
Set and remove tags
${tc}= Check Test Tags ${TESTNAME} tag1 tag2
- Check Log Message ${tc.kws[0].msgs[0]} Removed tag 'force?'.
- Check Log Message ${tc.kws[0].msgs[1]} Set tags 'tag1' and 'tag2'.
- Check Log Message ${tc.kws[0].msgs[2]} ${PREFIX}Message
+ Check Log Message ${tc[0, 0]} Removed tag 'force?'.
+ Check Log Message ${tc[0, 1]} Set tags 'tag1' and 'tag2'.
+ Check Log Message ${tc[0, 2]} ${PREFIX}Message
Set tags are not removed
${tc}= Check Test Tags ${TESTNAME} force1 force2 tag1 tag2
- Check Log Message ${tc.kws[0].msgs[0]} Removed tag 'tag?'.
- Check Log Message ${tc.kws[0].msgs[1]} Set tags 'tag1' and 'tag2'.
- Check Log Message ${tc.kws[0].msgs[2]} ${PREFIX}Message
+ Check Log Message ${tc[0, 0]} Removed tag 'tag?'.
+ Check Log Message ${tc[0, 1]} Set tags 'tag1' and 'tag2'.
+ Check Log Message ${tc[0, 2]} ${PREFIX}Message
Set tags in teardown
${tc}= Check Test Tags ${TESTNAME} tag1 tag2
- Check Log Message ${tc.teardown.msgs[0]} Removed tag 'force?'.
- Check Log Message ${tc.teardown.msgs[1]} Set tags 'tag1' and 'tag2'.
- Check Log Message ${tc.teardown.msgs[2]} ${PREFIX}Message
+ Check Log Message ${tc.teardown[0]} Removed tag 'force?'.
+ Check Log Message ${tc.teardown[1]} Set tags 'tag1' and 'tag2'.
+ Check Log Message ${tc.teardown[2]} ${PREFIX}Message
Pass Execution If when condition is true
Check Test Case ${TESTNAME}
Pass Execution If when condition is false
${tc} = Check Test Case ${TESTNAME}
- Keyword Should Have Been Executed ${tc.kws[1]}
+ Keyword Should Have Been Executed ${tc[1]}
Pass Execution If resolves variables only condition is true
${tc} = Check Test Case ${TESTNAME}
- Keyword Should Have Been Executed ${tc.kws[1]}
+ Keyword Should Have Been Executed ${tc[1]}
Pass Execution If with multiple variables
Check Test Tags ${TESTNAME} force1 force2 my tags
diff --git a/atest/robot/running/prevent_recursion.robot b/atest/robot/running/prevent_recursion.robot
deleted file mode 100644
index 8641c4e476d..00000000000
--- a/atest/robot/running/prevent_recursion.robot
+++ /dev/null
@@ -1,21 +0,0 @@
-*** Settings ***
-Suite Setup Run Tests ${EMPTY} running/prevent_recursion.robot
-Resource atest_resource.robot
-
-*** Test Cases ***
-Infinite recursion
- Check Test Case ${TESTNAME}
-
-Infinite cyclic recursion
- Check Test Case ${TESTNAME}
-
-Infinite recursion with Run Keyword
- Check Test Case ${TESTNAME}
-
-Infinitely recursive for loop
- Check Test Case ${TESTNAME}
-
-Recursion below the recursion limit is ok
- [Documentation] Also verifies that recursion limit blown earlier doesn't affect subsequent tests
- Check Test Case ${TESTNAME}
-
diff --git a/atest/robot/running/return.robot b/atest/robot/running/return.robot
index a9da6755056..a94de10b833 100644
--- a/atest/robot/running/return.robot
+++ b/atest/robot/running/return.robot
@@ -5,51 +5,51 @@ Resource atest_resource.robot
*** Test Cases ***
Simple
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.body[0].body[1].type} RETURN
- Should Be Equal ${tc.body[0].body[1].values} ${{()}}
- Should Be Equal ${tc.body[0].body[1].status} PASS
- Should Be Equal ${tc.body[0].body[1].message} ${EMPTY}
- Should Be Equal ${tc.body[0].body[2].status} NOT RUN
- Should Be Equal ${tc.body[0].message} ${EMPTY}
+ Should Be Equal ${tc[0, 1].type} RETURN
+ Should Be Equal ${tc[0, 1].values} ${{()}}
+ Should Be Equal ${tc[0, 1].status} PASS
+ Should Be Equal ${tc[0, 1].message} ${EMPTY}
+ Should Be Equal ${tc[0, 2].status} NOT RUN
+ Should Be Equal ${tc[0].message} ${EMPTY}
Return value
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.body[0].body[0].type} RETURN
- Should Be Equal ${tc.body[0].body[0].values} ${{('value',)}}
+ Should Be Equal ${tc[0, 0].type} RETURN
+ Should Be Equal ${tc[0, 0].values} ${{('value',)}}
Return value as variable
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.body[0].body[0].type} RETURN
- Should Be Equal ${tc.body[0].body[0].values} ${{('\${42}',)}}
+ Should Be Equal ${tc[0, 0].type} RETURN
+ Should Be Equal ${tc[0, 0].values} ${{('\${42}',)}}
Return multiple values
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.body[0].body[0].type} RETURN
- Should Be Equal ${tc.body[0].body[0].values} ${{('first', '\${2}', 'third')}}
+ Should Be Equal ${tc[0, 0].type} RETURN
+ Should Be Equal ${tc[0, 0].values} ${{('first', '\${2}', 'third')}}
In nested keyword
Check Test Case ${TESTNAME}
In IF
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.body[0].body[0].body[0].body[0].type} RETURN
- Should Be Equal ${tc.body[0].body[0].body[0].body[0].status} PASS
- Should Be Equal ${tc.body[0].body[0].body[0].body[1].status} NOT RUN
- Should Be Equal ${tc.body[0].body[1].status} NOT RUN
- Should Be Equal ${tc.body[2].body[0].body[1].body[0].type} RETURN
- Should Be Equal ${tc.body[2].body[0].body[1].body[0].status} PASS
- Should Be Equal ${tc.body[2].body[0].body[1].body[1].status} NOT RUN
- Should Be Equal ${tc.body[2].body[1].status} NOT RUN
+ Should Be Equal ${tc[0, 0, 0, 0].type} RETURN
+ Should Be Equal ${tc[0, 0, 0, 0].status} PASS
+ Should Be Equal ${tc[0, 0, 0, 1].status} NOT RUN
+ Should Be Equal ${tc[0, 1].status} NOT RUN
+ Should Be Equal ${tc[2, 0, 1, 0].type} RETURN
+ Should Be Equal ${tc[2, 0, 1, 0].status} PASS
+ Should Be Equal ${tc[2, 0, 1, 1].status} NOT RUN
+ Should Be Equal ${tc[2, 1].status} NOT RUN
In inline IF
Check Test Case ${TESTNAME}
In FOR
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.body[0].body[0].body[0].body[0].type} RETURN
- Should Be Equal ${tc.body[0].body[0].body[0].body[0].status} PASS
- Should Be Equal ${tc.body[0].body[0].body[0].body[1].status} NOT RUN
- Should Be Equal ${tc.body[0].body[1].status} NOT RUN
+ Should Be Equal ${tc[0, 0, 0, 0].type} RETURN
+ Should Be Equal ${tc[0, 0, 0, 0].status} PASS
+ Should Be Equal ${tc[0, 0, 0, 1].status} NOT RUN
+ Should Be Equal ${tc[0, 1].status} NOT RUN
In nested FOR/IF structure
Check Test Case ${TESTNAME}
diff --git a/atest/robot/running/return_from_keyword.robot b/atest/robot/running/return_from_keyword.robot
index 4c9c2fa72b6..34b905cfa3c 100644
--- a/atest/robot/running/return_from_keyword.robot
+++ b/atest/robot/running/return_from_keyword.robot
@@ -56,5 +56,5 @@ Return From Keyword If does not evaluate bogus arguments if condition is untrue
Logs Info
${tc} = Check Test Case Without Return Value
- Check Log Message ${tc.kws[0].kws[0].msgs[0]}
+ Check Log Message ${tc[0, 0, 0]}
... Returning from the enclosing user keyword.
diff --git a/atest/robot/running/setup_and_teardown_using_embedded_arguments.robot b/atest/robot/running/setup_and_teardown_using_embedded_arguments.robot
new file mode 100644
index 00000000000..04fa1b5d44e
--- /dev/null
+++ b/atest/robot/running/setup_and_teardown_using_embedded_arguments.robot
@@ -0,0 +1,46 @@
+*** Settings ***
+Suite Setup Run Tests ${EMPTY} running/setup_and_teardown_using_embedded_arguments.robot
+Resource atest_resource.robot
+
+*** Test Cases ***
+Suite setup and teardown
+ Should Be Equal ${SUITE.setup.name} Embedded "arg"
+ Should Be Equal ${SUITE.teardown.name} Object \${LIST}
+
+Test setup and teardown
+ ${tc} = Check Test Case ${TESTNAME}
+ Should Be Equal ${tc.setup.name} Embedded "arg"
+ Should Be Equal ${tc.teardown.name} Embedded "arg"
+
+Keyword setup and teardown
+ ${tc} = Check Test Case ${TESTNAME}
+ Should Be Equal ${tc[0].setup.name} Embedded "arg"
+ Should Be Equal ${tc[0].teardown.name} Embedded "arg"
+
+Argument as variable
+ ${tc} = Check Test Case ${TESTNAME}
+ Should Be Equal ${tc.setup.name} Embedded "\${ARG}"
+ Should Be Equal ${tc[0].setup.name} Embedded "\${ARG}"
+ Should Be Equal ${tc[0].teardown.name} Embedded "\${ARG}"
+ Should Be Equal ${tc.teardown.name} Embedded "\${ARG}"
+
+Argument as non-string variable
+ ${tc} = Check Test Case ${TESTNAME}
+ Should Be Equal ${tc.setup.name} Object \${LIST}
+ Should Be Equal ${tc[0].setup.name} Object \${LIST}
+ Should Be Equal ${tc[0].teardown.name} Object \${LIST}
+ Should Be Equal ${tc.teardown.name} Object \${LIST}
+
+Argument matching only after replacing variables
+ ${tc} = Check Test Case ${TESTNAME}
+ Should Be Equal ${tc.setup.name} Embedded "arg"
+ Should Be Equal ${tc[0].setup.name} Embedded "arg"
+ Should Be Equal ${tc[0].teardown.name} Embedded "arg"
+ Should Be Equal ${tc.teardown.name} Embedded "arg"
+
+Exact match after replacing variables has higher precedence
+ ${tc} = Check Test Case ${TESTNAME}
+ Should Be Equal ${tc.setup.name} Embedded not, exact match instead
+ Should Be Equal ${tc[0].setup.name} Embedded not, exact match instead
+ Should Be Equal ${tc[0].teardown.name} Embedded not, exact match instead
+ Should Be Equal ${tc.teardown.name} Embedded not, exact match instead
diff --git a/atest/robot/running/skip.robot b/atest/robot/running/skip.robot
index 886a5f26b03..1af592e235f 100644
--- a/atest/robot/running/skip.robot
+++ b/atest/robot/running/skip.robot
@@ -44,7 +44,7 @@ Skip in Teardown After Failure In Body
Teardown is executed after skip
${tc} = Check Test Case ${TEST NAME}
- Check log message ${tc.teardown.msgs[0]} Teardown is executed!
+ Check log message ${tc.teardown[0]} Teardown is executed!
Fail in Teardown After Skip In Body
Check Test Case ${TEST NAME}
@@ -110,27 +110,39 @@ Skip with Wait Until Keyword Succeeds
Skipped with --skip
Check Test Case ${TEST NAME}
-Skipped when test is tagged with robot:skip
+Skipped with --skip when tag uses variable
+ Check Test Case ${TEST NAME}
+
+Skipped with robot:skip
+ Check Test Case ${TEST NAME}
+
+Skipped with robot:skip when tag uses variable
Check Test Case ${TEST NAME}
Skipped with --SkipOnFailure
Check Test Case ${TEST NAME}
-Skipped with --SkipOnFailure when Failure in Test Setup
+Skipped with --SkipOnFailure when tag uses variable
Check Test Case ${TEST NAME}
-Skipped with --SkipOnFailure when Failure in Test Teardown
+Skipped with --SkipOnFailure when failure in setup
Check Test Case ${TEST NAME}
-Skipped with --SkipOnFailure when Set Tags Used in Teardown
+Skipped with --SkipOnFailure when failure in teardown
Check Test Case ${TEST NAME}
-Skipped although test fails since test is tagged with robot:skip-on-failure
+Skipped with --SkipOnFailure when Set Tags used in teardown
Check Test Case ${TEST NAME}
-Using Skip Does Not Affect Passing And Failing Tests
- Check Test Case Passing Test
- Check Test Case Failing Test
+Skipped with robot:skip-on-failure
+ Check Test Case ${TEST NAME}
+
+Skipped with robot:skip-on-failure when tag uses variable
+ Check Test Case ${TEST NAME}
+
+Skipping does not affect passing and failing tests
+ Check Test Case Passing
+ Check Test Case Failing
Suite setup and teardown are not run if all tests are unconditionally skipped or excluded
${suite} = Get Test Suite All Skipped
@@ -139,3 +151,17 @@ Suite setup and teardown are not run if all tests are unconditionally skipped or
Check Test Case Skip using robot:skip
Check Test Case Skip using --skip
Length Should Be ${suite.suites[0].tests} 2
+
+--skip and --skip-on-failure used multiple times
+ Run Tests --skip skip-this --skip no-match --SkipOnFailure skip-on-failure --skip-on-failure xxx running/skip/skip.robot
+ Check Test Case Skipped with --skip
+ ... message=Test skipped using 'no-match' and 'skip-this' tags.
+ Check Test Case Skipped with --SkipOnFailure
+ ... message=Failed test skipped using 'skip-on-failure' and 'xxx' tags.\n\nOriginal failure:\nOoops, we fail!
+
+--skip and --skip-on-failure with patterns
+ Run Tests --skip skip-t*s --skip no-match --SkipOnFailure xxxORskip-on-failure running/skip/skip.robot
+ Check Test Case Skipped with --skip
+ ... message=Test skipped using 'no-match' and 'skip-t*s' tag patterns.
+ Check Test Case Skipped with --SkipOnFailure
+ ... message=Failed test skipped using 'xxx OR skip-on-failure' tag pattern.\n\nOriginal failure:\nOoops, we fail!
diff --git a/atest/robot/running/skip_in_rpa_mode.robot b/atest/robot/running/skip_in_rpa_mode.robot
index e5370328d05..6da7ef27bfd 100644
--- a/atest/robot/running/skip_in_rpa_mode.robot
+++ b/atest/robot/running/skip_in_rpa_mode.robot
@@ -1,5 +1,5 @@
*** Settings ***
-Suite Setup Run Tests --rpa --skip skip-this --SkipOnFailure skip-on-failure --variable test_or_task:Task running/skip/
+Suite Setup Run Tests --rpa --skip skip-this --SkipOnFailure skip-on-failure --variable test_or_task:task running/skip/
Resource atest_resource.robot
*** Test Cases ***
@@ -8,4 +8,3 @@ Skipped with --skip
Skipped with --SkipOnFailure
Check Test Case ${TEST NAME}
-
diff --git a/atest/robot/running/skip_with_template.robot b/atest/robot/running/skip_with_template.robot
new file mode 100644
index 00000000000..a642c665146
--- /dev/null
+++ b/atest/robot/running/skip_with_template.robot
@@ -0,0 +1,71 @@
+*** Settings ***
+Suite Setup Run Tests ${EMPTY} running/skip_with_template.robot
+Resource atest_resource.robot
+
+*** Test Cases ***
+SKIP + PASS -> PASS
+ ${tc} = Check Test Case ${TEST NAME}
+ Status Should Be ${tc[0]} SKIP Skipped
+ Status Should Be ${tc[1]} PASS
+
+FAIL + ANY -> FAIL
+ ${tc} = Check Test Case ${TEST NAME}
+ Status Should Be ${tc[0]} PASS
+ Status Should Be ${tc[1]} SKIP Skipped
+ Status Should Be ${tc[2]} PASS
+ Status Should Be ${tc[3]} FAIL Failed
+ Status Should Be ${tc[4]} SKIP Skipped
+
+Only SKIP -> SKIP
+ ${tc} = Check Test Case ${TEST NAME}
+ Status Should Be ${tc[0]} SKIP Skipped
+ Status Should Be ${tc[1]} SKIP Skipped
+
+IF w/ SKIP + PASS -> PASS
+ ${tc} = Check Test Case ${TEST NAME}
+ Status Should Be ${tc[0]} PASS
+ Status Should Be ${tc[1]} SKIP Skipped
+ Status Should Be ${tc[2]} PASS
+
+IF w/ FAIL + ANY -> FAIL
+ ${tc} = Check Test Case ${TEST NAME}
+ Status Should Be ${tc[0]} FAIL Failed
+ Status Should Be ${tc[1]} SKIP Skipped
+ Status Should Be ${tc[2]} PASS
+
+IF w/ only SKIP -> SKIP
+ ${tc} = Check Test Case ${TEST NAME}
+ Status Should Be ${tc[0]} SKIP All iterations skipped.
+ Status Should Be ${tc[1]} SKIP Skip 3
+ Status Should Be ${tc[2]} SKIP Skip 4
+
+FOR w/ SKIP + PASS -> PASS
+ ${tc} = Check Test Case ${TEST NAME}
+ Status Should Be ${tc[0]} PASS
+ Status Should Be ${tc[1]} SKIP just once
+ Status Should Be ${tc[2]} PASS
+
+FOR w/ FAIL + ANY -> FAIL
+ ${tc} = Check Test Case ${TEST NAME}
+ Status Should Be ${tc[0]} FAIL Several failures occurred:\n\n1) a\n\n2) b
+ Status Should Be ${tc[1]} SKIP just once
+ Status Should Be ${tc[2]} PASS
+
+FOR w/ only SKIP -> SKIP
+ ${tc} = Check Test Case ${TEST NAME}
+ Status Should Be ${tc[0]} SKIP All iterations skipped.
+ Status Should Be ${tc[1]} SKIP just once
+
+Messages in test body are ignored
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Log Message ${tc[0]} Hello 'Messages in test body are ignored', says listener!
+ Check Log Message ${tc[1, 0, 0]} Library listener adds messages to body of this test.
+ Check Log Message ${tc[2, 0, 0]} This iteration is skipped! SKIP
+ Check Log Message ${tc[3, 0, 0]} This iteration passes!
+ Check Log Message ${tc[4]} Bye 'Messages in test body are ignored', says listener!
+
+*** Keywords ***
+Status Should Be
+ [Arguments] ${item} ${status} ${message}=
+ Should Be Equal ${item.status} ${status}
+ Should Be Equal ${item.message} ${message}
diff --git a/atest/robot/running/steps_after_failure.robot b/atest/robot/running/steps_after_failure.robot
index 30a1de2dc96..602f40d3001 100644
--- a/atest/robot/running/steps_after_failure.robot
+++ b/atest/robot/running/steps_after_failure.robot
@@ -5,143 +5,159 @@ Resource atest_resource.robot
*** Test Cases ***
Library keyword after failure
${tc} = Check Test Case ${TESTNAME}
- Should Not Be Run ${tc.body[2:]} 5
- Check Log Message ${tc.teardown.msgs[0]} This is run
+ Should Not Be Run ${tc[2:]} 5
+ Check Log Message ${tc.teardown[0]} This is run
User keyword after failure
${tc} = Check Test Case ${TESTNAME}
- Should Not Be Run ${tc.body[1:]}
+ Should Not Be Run ${tc[1:]}
Non-existing keyword after failure
${tc} = Check Test Case ${TESTNAME}
- Should Not Be Run ${tc.body[1:]}
+ Should Not Be Run ${tc[1:]}
Invalid keyword usage after failure
${tc} = Check Test Case ${TESTNAME}
- Should Not Be Run ${tc.body[1:]}
+ Should Not Be Run ${tc[1:]}
Assignment after failure
${tc} = Check Test Case ${TESTNAME}
- Should Not Be Run ${tc.body[1:]} 4
- Check Keyword Data ${tc.body[1]} Not run assign=\${x} status=NOT RUN
- Check Keyword Data ${tc.body[2]} Not run assign=\${x} status=NOT RUN
- Check Keyword Data ${tc.body[3]} Not run assign=\${x}, \${y} status=NOT RUN
- Check Keyword Data ${tc.body[4]} Not run assign=\${x}, \${y} status=NOT RUN
+ Should Not Be Run ${tc[1:]} 4
+ Check Keyword Data ${tc[1]} Not run assign=\${x} status=NOT RUN
+ Check Keyword Data ${tc[2]} Not run assign=\${x} status=NOT RUN
+ Check Keyword Data ${tc[3]} Not run assign=\${x}, \${y} status=NOT RUN
+ Check Keyword Data ${tc[4]} Not run assign=\${x}, \${y} status=NOT RUN
IF after failure
${tc} = Check Test Case ${TESTNAME}
- Should Not Be Run ${tc.body[1:]}
- Should Not Be Run ${tc.body[1].body[0].body}
- Should Not Be Run ${tc.body[1].body[1].body}
- Check Keyword Data ${tc.body[1].body[1].body[0]}
+ Should Not Be Run ${tc[1:]}
+ Should Not Be Run ${tc[1, 0].body}
+ Should Not Be Run ${tc[1, 1].body}
+ Check Keyword Data ${tc[1, 1, 0]}
+ ... BuiltIn.Fail assign=\${x} args=This should not be run status=NOT RUN
+
+GROUP after failure
+ ${tc} = Check Test Case ${TESTNAME}
+ Should Not Be Run ${tc[1:]}
+ Should Not Be Run ${tc[1].body} 2
+ Check Keyword Data ${tc[1, 1]}
... BuiltIn.Fail assign=\${x} args=This should not be run status=NOT RUN
FOR after failure
${tc} = Check Test Case ${TESTNAME}
- Should Not Be Run ${tc.body[1:]}
- Should Not Be Run ${tc.body[1].body}
- Should Not Be Run ${tc.body[1].body[0].body} 2
- Check Keyword Data ${tc.body[1].body[0].body[1]}
+ Should Not Be Run ${tc[1:]}
+ Should Not Be Run ${tc[1].body}
+ Should Not Be Run ${tc[1, 0].body} 2
+ Check Keyword Data ${tc[1, 0, 1]}
... BuiltIn.Fail assign=\${x} args=This should not be run either status=NOT RUN
TRY after failure
${tc} = Check Test Case ${TESTNAME}
- Should Not Be Run ${tc.body[1:]}
- Should Not Be Run ${tc.body[1].body} 4
- FOR ${step} IN @{tc.body[1].body}
+ Should Not Be Run ${tc[1:]}
+ Should Not Be Run ${tc[1].body} 4
+ FOR ${step} IN @{tc[1].body}
Should Not Be Run ${step.body}
END
WHILE after failure
${tc} = Check Test Case ${TESTNAME}
- Should Not Be Run ${tc.body[1:]} 3
- Should Not Be Run ${tc.body[1].body}
- Should Not Be Run ${tc.body[1].body[0].body} 3
- Should Not Be Run ${tc.body[2].body}
- Should Not Be Run ${tc.body[2].body[0].body} 2
- Should Not Be Run ${tc.body[3].body}
- Should Not Be Run ${tc.body[3].body[0].body} 1
+ Should Not Be Run ${tc[1:]} 3
+ Should Not Be Run ${tc[1].body}
+ Should Not Be Run ${tc[1, 0].body} 3
+ Should Not Be Run ${tc[2].body}
+ Should Not Be Run ${tc[2, 0].body} 2
+ Should Not Be Run ${tc[3].body}
+ Should Not Be Run ${tc[3, 0].body} 1
RETURN after failure
${tc} = Check Test Case ${TESTNAME}
- Should Not Be Run ${tc.body[1:]}
- Should Not Be Run ${tc.body[0].body[1:]} 2
- Should Be Equal ${tc.body[0].body[1].type} RETURN
+ Should Not Be Run ${tc[1:]}
+ Should Not Be Run ${tc[0][1:]} 2
+ Should Be Equal ${tc[0, 1].type} RETURN
BREAK and CONTINUE after failure
${tc} = Check Test Case ${TESTNAME}
- Should Not Be Run ${tc.body[1:]} 1
- Should Not Be Run ${tc.body[0].body[0].body[1:]} 2
- Should Not Be Run ${tc.body[1].body}
- Should Not Be Run ${tc.body[1].body[0].body} 2
+ Should Not Be Run ${tc[1:]} 1
+ Should Not Be Run ${tc[0, 0][1:]} 2
+ Should Not Be Run ${tc[1].body}
+ Should Not Be Run ${tc[1, 0].body} 2
Nested control structure after failure
${tc} = Check Test Case ${TESTNAME}
- Should Not Be Run ${tc.body[1:]} 2
- Should Be Equal ${tc.body[1].type} FOR
- Should Not Be Run ${tc.body[1].body} 1
- Should Be Equal ${tc.body[1].body[0].type} ITERATION
- Should Not Be Run ${tc.body[1].body[0].body} 2
- Should Be Equal ${tc.body[1].body[0].body[0].type} IF/ELSE ROOT
- Should Not Be Run ${tc.body[1].body[0].body[0].body} 2
- Should Be Equal ${tc.body[1].body[0].body[0].body[0].type} IF
- Should Not Be Run ${tc.body[1].body[0].body[0].body[0].body} 2
- Should Be Equal ${tc.body[1].body[0].body[0].body[0].body[0].type} FOR
- Should Not Be Run ${tc.body[1].body[0].body[0].body[0].body[0].body} 1
- Should Be Equal ${tc.body[1].body[0].body[0].body[0].body[0].body[0].type} ITERATION
- Should Not Be Run ${tc.body[1].body[0].body[0].body[0].body[0].body[0].body} 3
- Should Be Equal ${tc.body[1].body[0].body[0].body[0].body[0].body[0].body[0].type} KEYWORD
- Should Be Equal ${tc.body[1].body[0].body[0].body[0].body[0].body[0].body[1].type} KEYWORD
- Should Be Equal ${tc.body[1].body[0].body[0].body[0].body[0].body[0].body[2].type} KEYWORD
- Should Be Equal ${tc.body[1].body[0].body[0].body[0].body[1].type} KEYWORD
- Should Be Equal ${tc.body[1].body[0].body[0].body[1].type} ELSE
- Should Not Be Run ${tc.body[1].body[0].body[0].body[1].body} 2
- Should Be Equal ${tc.body[1].body[0].body[0].body[1].body[0].type} WHILE
- Should Not Be Run ${tc.body[1].body[0].body[0].body[1].body[0].body} 1
- Should Be Equal ${tc.body[1].body[0].body[0].body[1].body[0].body[0].type} ITERATION
- Should Not Be Run ${tc.body[1].body[0].body[0].body[1].body[0].body[0].body} 2
- Should Be Equal ${tc.body[1].body[0].body[0].body[1].body[0].body[0].body[0].type} KEYWORD
- Should Be Equal ${tc.body[1].body[0].body[0].body[1].body[0].body[0].body[1].type} KEYWORD
- Should Be Equal ${tc.body[1].body[0].body[0].body[1].body[1].type} TRY/EXCEPT ROOT
- Should Not Be Run ${tc.body[1].body[0].body[0].body[1].body[1].body} 2
- Should Be Equal ${tc.body[1].body[0].body[0].body[1].body[1].body[0].type} TRY
- Should Not Be Run ${tc.body[1].body[0].body[0].body[1].body[1].body[0].body} 1
- Should Be Equal ${tc.body[1].body[0].body[0].body[1].body[1].body[0].body[0].type} KEYWORD
- Should Be Equal ${tc.body[1].body[0].body[0].body[1].body[1].body[1].type} EXCEPT
- Should Not Be Run ${tc.body[1].body[0].body[0].body[1].body[1].body[1].body} 1
- Should Be Equal ${tc.body[1].body[0].body[0].body[1].body[1].body[1].body[0].type} BREAK
- Should Be Equal ${tc.body[1].body[0].body[1].type} KEYWORD
- Should Be Equal ${tc.body[2].type} KEYWORD
+ Should Not Be Run ${tc[1:]} 2
+ Should Be Equal ${tc[1].type} FOR
+ Should Not Be Run ${tc[1].body} 1
+ Should Be Equal ${tc[1, 0].type} ITERATION
+ Should Not Be Run ${tc[1, 0].body} 2
+ Should Be Equal ${tc[1, 0, 0].type} IF/ELSE ROOT
+ Should Not Be Run ${tc[1, 0, 0].body} 2
+ Should Be Equal ${tc[1, 0, 0, 0].type} IF
+ Should Not Be Run ${tc[1, 0, 0, 0].body} 2
+ Should Be Equal ${tc[1, 0, 0, 0, 0].type} FOR
+ Should Not Be Run ${tc[1, 0, 0, 0, 0].body} 1
+ Should Be Equal ${tc[1, 0, 0, 0, 0, 0].type} ITERATION
+ Should Not Be Run ${tc[1, 0, 0, 0, 0, 0].body} 2
+ Should Be Equal ${tc[1, 0, 0, 0, 0, 0, 0].type} KEYWORD
+ Should Be Equal ${tc[1, 0, 0, 0, 0, 0, 1].type} GROUP
+ Should Not Be Run ${tc[1, 0, 0, 0, 0, 0, 1].body} 2
+ Should Be Equal ${tc[1, 0, 0, 0, 0, 0, 1, 0].type} KEYWORD
+ Should Be Equal ${tc[1, 0, 0, 0, 0, 0, 1, 1].type} KEYWORD
+ Should Be Equal ${tc[1, 0, 0, 0, 1].type} KEYWORD
+ Should Be Equal ${tc[1, 0, 0, 1].type} ELSE
+ Should Not Be Run ${tc[1, 0, 0, 1].body} 2
+ Should Be Equal ${tc[1, 0, 0, 1, 0].type} WHILE
+ Should Not Be Run ${tc[1, 0, 0, 1, 0].body} 1
+ Should Be Equal ${tc[1, 0, 0, 1, 0, 0].type} ITERATION
+ Should Not Be Run ${tc[1, 0, 0, 1, 0, 0].body} 2
+ Should Be Equal ${tc[1, 0, 0, 1, 0, 0, 0].type} KEYWORD
+ Should Be Equal ${tc[1, 0, 0, 1, 0, 0, 1].type} KEYWORD
+ Should Be Equal ${tc[1, 0, 0, 1, 1].type} TRY/EXCEPT ROOT
+ Should Not Be Run ${tc[1, 0, 0, 1, 1].body} 2
+ Should Be Equal ${tc[1, 0, 0, 1, 1, 0].type} TRY
+ Should Not Be Run ${tc[1, 0, 0, 1, 1, 0].body} 1
+ Should Be Equal ${tc[1, 0, 0, 1, 1, 0, 0].type} KEYWORD
+ Should Be Equal ${tc[1, 0, 0, 1, 1, 1].type} EXCEPT
+ Should Not Be Run ${tc[1, 0, 0, 1, 1, 1].body} 1
+ Should Be Equal ${tc[1, 0, 0, 1, 1, 1, 0].type} BREAK
+ Should Be Equal ${tc[1, 0, 1].type} KEYWORD
+ Should Be Equal ${tc[2].type} KEYWORD
Failure in user keyword
${tc} = Check Test Case ${TESTNAME}
- Should Not Be Run ${tc.body[1:]}
- Should Not Be Run ${tc.body[0].body[1:]} 2
+ Should Not Be Run ${tc[1:]}
+ Should Not Be Run ${tc[0][1:]} 2
Failure in IF branch
${tc} = Check Test Case ${TESTNAME}
- Should Not Be Run ${tc.body[0].body[0].body[1:]}
- Should Not Be Run ${tc.body[0].body[1].body}
- Should Not Be Run ${tc.body[1:]}
+ Should Not Be Run ${tc[0, 0][1:]}
+ Should Not Be Run ${tc[0, 1].body}
+ Should Not Be Run ${tc[1:]}
Failure in ELSE IF branch
${tc} = Check Test Case ${TESTNAME}
- Should Not Be Run ${tc.body[0].body[0].body}
- Should Not Be Run ${tc.body[0].body[1].body[1:]}
- Should Not Be Run ${tc.body[0].body[2].body}
- Should Not Be Run ${tc.body[1:]}
+ Should Not Be Run ${tc[0, 0].body}
+ Should Not Be Run ${tc[0, 1][1:]}
+ Should Not Be Run ${tc[0, 2].body}
+ Should Not Be Run ${tc[1:]}
Failure in ELSE branch
${tc} = Check Test Case ${TESTNAME}
- Should Not Be Run ${tc.body[0].body[0].body}
- Should Not Be Run ${tc.body[0].body[1].body[1:]}
- Should Not Be Run ${tc.body[1:]}
+ Should Not Be Run ${tc[0, 0].body}
+ Should Not Be Run ${tc[0, 1][1:]}
+ Should Not Be Run ${tc[1:]}
+
+Failure in GROUP
+ ${tc} = Check Test Case ${TESTNAME}
+ Should Not Be Run ${tc[0, 0][1:]}
+ Should Not Be Run ${tc[0][1:]} 2
+ Should Not Be Run ${tc[0, 2].body}
+ Should Not Be Run ${tc[1:]}
Failure in FOR iteration
${tc} = Check Test Case ${TESTNAME}
- Should Not Be Run ${tc.body[1:]}
- Length Should Be ${tc.body[0].body} 1
- Should Not Be Run ${tc.body[0].body[0].body[1:]}
+ Should Not Be Run ${tc[1:]}
+ Length Should Be ${tc[0].body} 1
+ Should Not Be Run ${tc[0, 0][1:]}
*** Keywords ***
Should Not Be Run
diff --git a/atest/robot/running/stopping_with_signal.robot b/atest/robot/running/stopping_with_signal.robot
index 15043d4bc59..fe79715a963 100644
--- a/atest/robot/running/stopping_with_signal.robot
+++ b/atest/robot/running/stopping_with_signal.robot
@@ -59,8 +59,8 @@ One Signal Should Stop Test Execution Gracefully And Test Case And Suite Teardow
Start And Send Signal with_teardown.robot One SIGINT
Check Test Cases Have Failed Correctly
${tc} = Get Test Case Test
- Check Log Message ${tc.teardown.msgs[0]} Logging Test Case Teardown
- Check Log Message ${SUITE.teardown.kws[0].msgs[0]} Logging Suite Teardown
+ Check Log Message ${tc.teardown[0]} Logging Test Case Teardown
+ Check Log Message ${SUITE.teardown[0, 0]} Logging Suite Teardown
Skip Teardowns After Stopping Gracefully
Start And Send Signal with_teardown.robot One SIGINT 0s --SkipTeardownOnExit
@@ -73,9 +73,9 @@ SIGINT Signal Should Stop Async Test Execution Gracefully
Start And Send Signal async_stop.robot One SIGINT 5
Check Test Cases Have Failed Correctly
${tc} = Get Test Case Test
- Evaluate len(${tc.kws[1].msgs}) == 1
- Check Log Message ${tc.kws[1].msgs[0]} Start Sleep
- Evaluate len(${SUITE.teardown.msgs}) == 0
+ Length Should Be ${tc[1].body} 1
+ Check Log Message ${tc[1, 0]} Start Sleep
+ Length Should Be ${SUITE.teardown.body} 0
Two SIGINT Signals Should Stop Async Test Execution Forcefully
Start And Send Signal async_stop.robot Two SIGINTs 5
@@ -86,15 +86,26 @@ SIGTERM Signal Should Stop Async Test Execution Gracefully
Start And Send Signal async_stop.robot One SIGTERM 5
Check Test Cases Have Failed Correctly
${tc} = Get Test Case Test
- Evaluate len(${tc.kws[1].msgs}) == 1
- Check Log Message ${tc.kws[1].msgs[0]} Start Sleep
- Evaluate len(${SUITE.teardown.msgs}) == 0
+ Length Should Be ${tc[1].body} 1
+ Check Log Message ${tc[1, 0]} Start Sleep
+ Length Should Be ${SUITE.teardown.body} 0
Two SIGTERM Signals Should Stop Async Test Execution Forcefully
[Tags] no-windows
Start And Send Signal async_stop.robot Two SIGTERMs 5
Check Tests Have Been Forced To Shutdown
+Signal handler is reset after execution
+ [Tags] no-windows
+ ${result} = Run Process
+ ... @{INTERPRETER.interpreter}
+ ... ${DATADIR}/running/stopping_with_signal/test_signalhandler_is_reset.py
+ ... stderr=STDOUT
+ ... env:PYTHONPATH=${INTERPRETER.src_dir}
+ Log ${result.stdout}
+ Should Contain X Times ${result.stdout} Execution terminated by signal count=1
+ Should Be Equal ${result.rc} ${0}
+
*** Keywords ***
Start And Send Signal
[Arguments] ${datasource} ${signals} ${sleep}=0s @{extra options}
diff --git a/atest/robot/running/test_case_status.robot b/atest/robot/running/test_case_status.robot
index cba04ca835d..55e4ae27b1a 100644
--- a/atest/robot/running/test_case_status.robot
+++ b/atest/robot/running/test_case_status.robot
@@ -41,12 +41,10 @@ Test Setup And Teardown Pass
Check Test Case ${TEST NAME}
Test Teardown is Run When Setup Fails
- ${test} Check Test Case ${TEST NAME}
- ${td} = Set Variable ${test.teardown}
- Should Not Be Equal ${td} ${None} Teardown not run No values
- Length Should Be ${td.msgs} 1
- Check Log Message ${td.msgs[0]} Hello from teardown!
- Length Should Be ${td.kws} 0
+ ${tc} = Check Test Case ${TEST NAME}
+ Should Not Be Equal ${tc.teardown} ${None} Teardown not run No values
+ Length Should Be ${tc.teardown.body} 1
+ Check Log Message ${tc.teardown[0]} Hello from teardown!
Test Setup And Teardown Fails
Check Test Case ${TEST NAME}
diff --git a/atest/robot/running/test_template.robot b/atest/robot/running/test_template.robot
index a6faecac8dd..ba2a2e22941 100644
--- a/atest/robot/running/test_template.robot
+++ b/atest/robot/running/test_template.robot
@@ -59,32 +59,32 @@ Invalid FOR
Template With IF
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.body[0].status} PASS
- Should Be Equal ${tc.body[0].body[0].type} IF
- Should Be Equal ${tc.body[0].body[0].status} NOT RUN
- Should Be Equal ${tc.body[0].body[1].type} ELSE IF
- Should Be Equal ${tc.body[0].body[1].status} NOT RUN
- Should Be Equal ${tc.body[0].body[2].type} ELSE
- Should Be Equal ${tc.body[0].body[2].status} PASS
+ Should Be Equal ${tc[0].status} PASS
+ Should Be Equal ${tc[0, 0].type} IF
+ Should Be Equal ${tc[0, 0].status} NOT RUN
+ Should Be Equal ${tc[0, 1].type} ELSE IF
+ Should Be Equal ${tc[0, 1].status} NOT RUN
+ Should Be Equal ${tc[0, 2].type} ELSE
+ Should Be Equal ${tc[0, 2].status} PASS
Template With IF Failing
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.body[0].status} FAIL
- Should Be Equal ${tc.body[0].body[0].type} IF
- Should Be Equal ${tc.body[0].body[0].status} FAIL
- Should Be Equal ${tc.body[1].status} FAIL
- Should Be Equal ${tc.body[1].body[0].type} IF
- Should Be Equal ${tc.body[1].body[0].status} NOT RUN
- Should Be Equal ${tc.body[1].body[1].type} ELSE IF
- Should Be Equal ${tc.body[1].body[1].status} FAIL
- Should Be Equal ${tc.body[1].body[2].type} ELSE
- Should Be Equal ${tc.body[1].body[2].status} NOT RUN
+ Should Be Equal ${tc[0].status} FAIL
+ Should Be Equal ${tc[0, 0].type} IF
+ Should Be Equal ${tc[0, 0].status} FAIL
+ Should Be Equal ${tc[1].status} FAIL
+ Should Be Equal ${tc[1, 0].type} IF
+ Should Be Equal ${tc[1, 0].status} NOT RUN
+ Should Be Equal ${tc[1, 1].type} ELSE IF
+ Should Be Equal ${tc[1, 1].status} FAIL
+ Should Be Equal ${tc[1, 2].type} ELSE
+ Should Be Equal ${tc[1, 2].status} NOT RUN
Invalid IF
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.body[0].status} FAIL
- Should Be Equal ${tc.body[0].body[0].type} IF
- Should Be Equal ${tc.body[0].body[0].status} FAIL
+ Should Be Equal ${tc[0].status} FAIL
+ Should Be Equal ${tc[0, 0].type} IF
+ Should Be Equal ${tc[0, 0].status} FAIL
FOR and IF
Check Test Case ${TESTNAME}
diff --git a/atest/robot/running/test_template_with_embeded_args.robot b/atest/robot/running/test_template_with_embeded_args.robot
index 680406bce21..65b3d427abe 100644
--- a/atest/robot/running/test_template_with_embeded_args.robot
+++ b/atest/robot/running/test_template_with_embeded_args.robot
@@ -5,39 +5,39 @@ Resource atest_resource.robot
*** Test Cases ***
Matching arguments
${tc} = Check Test Case ${TESTNAME}
- Keyword should be ${tc.kws[0]} The result of 1 + 1 should be 2
- Keyword should be ${tc.kws[1]} The result of 1 + 2 should be 3
- Keyword should be ${tc.kws[2]} The result of 1 + 3 should be 5
+ Keyword should be ${tc[0]} The result of 1 + 1 should be 2
+ Keyword should be ${tc[1]} The result of 1 + 2 should be 3
+ Keyword should be ${tc[2]} The result of 1 + 3 should be 5
Argument names do not need to be same as in definition
${tc} = Check Test Case ${TESTNAME}
- Keyword should be ${tc.kws[0]} The result of 1 + 1 should be 2
- Keyword should be ${tc.kws[1]} The result of 1 + 2 should be 3
- Keyword should be ${tc.kws[2]} The result of 1 + 3 should be 5
+ Keyword should be ${tc[0]} The result of 1 + 1 should be 2
+ Keyword should be ${tc[1]} The result of 1 + 2 should be 3
+ Keyword should be ${tc[2]} The result of 1 + 3 should be 5
Some arguments can be hard-coded
${tc} = Check Test Case ${TESTNAME}
- Keyword should be ${tc.kws[0]} The result of 1 + 1 should be 3
- Keyword should be ${tc.kws[1]} The result of 1 + 2 should be 3
- Keyword should be ${tc.kws[2]} The result of 1 + 3 should be 3
+ Keyword should be ${tc[0]} The result of 1 + 1 should be 3
+ Keyword should be ${tc[1]} The result of 1 + 2 should be 3
+ Keyword should be ${tc[2]} The result of 1 + 3 should be 3
Can have different arguments than definition
${tc} = Check Test Case ${TESTNAME}
- Keyword should be ${tc.kws[0]} The result of 38 + 3 + 1 should be 42
- Keyword should be ${tc.kws[1]} The non-existing of 666 should be 42
+ Keyword should be ${tc[0]} The result of 38 + 3 + 1 should be 42
+ Keyword should be ${tc[1]} The non-existing of 666 should be 42
Can use variables
${tc} = Check Test Case ${TESTNAME}
- Keyword should be ${tc.kws[0]} The result of \${1} + \${2} should be \${3}
+ Keyword should be ${tc[0]} The result of \${1} + \${2} should be \${3}
Cannot have more arguments than variables
${tc} = Check Test Case ${TESTNAME}
- Keyword should be ${tc.kws[0]} The result of \${calc} should be 3
+ Keyword should be ${tc[0]} The result of \${calc} should be 3
... 1 + 2 extra
Cannot have less arguments than variables
${tc} = Check Test Case ${TESTNAME}
- Keyword should be ${tc.kws[0]} The result of \${calc} should be \${extra}
+ Keyword should be ${tc[0]} The result of \${calc} should be \${extra}
... 1 + 2
*** Keywords ***
diff --git a/atest/robot/running/timeouts.robot b/atest/robot/running/timeouts.robot
index d106d24919b..fa3d5ccec0a 100644
--- a/atest/robot/running/timeouts.robot
+++ b/atest/robot/running/timeouts.robot
@@ -23,7 +23,7 @@ Show Correct Traceback When Failing Before Timeout
... ${SPACE*2}File "*", line *, in exception
... ${SPACE*4}raise exception(msg)
... RuntimeError: Failure before timeout
- Check Log Message ${tc.kws[0].msgs[-1]} ${expected} DEBUG pattern=True traceback=True
+ Check Log Message ${tc[0, -1]} ${expected} DEBUG pattern=True traceback=True
Timeouted Test Timeouts
Check Test Case Sleeping And Timeouting
@@ -63,17 +63,17 @@ Test Timeouts When Also Keywords Are Timeouted
Keyword Timeout From Variable
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.kws[0].timeout} 1 millisecond
+ Should Be Equal ${tc[0].timeout} 1 millisecond
Keyword Timeout From Argument
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.kws[0].timeout} 1 second
- Should Be Equal ${tc.kws[1].timeout} 2 milliseconds
+ Should Be Equal ${tc[0].timeout} 1 second
+ Should Be Equal ${tc[1].timeout} 2 milliseconds
Embedded Arguments Timeout From Argument
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.kws[0].timeout} 1 second
- Should Be Equal ${tc.kws[1].timeout} 3 milliseconds
+ Should Be Equal ${tc[0].timeout} 1 second
+ Should Be Equal ${tc[1].timeout} 3 milliseconds
Local Variables Are Not Visible In Child Keyword Timeout
Check Test Case ${TEST NAME}
@@ -88,9 +88,9 @@ Test Timeout During Setup
Teardown After Test Timeout
[Documentation] Test that teardown is executed after a test has timed out
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.teardown.msgs[0]} Teardown executed
+ Check Log Message ${tc.teardown[0]} Teardown executed
${tc} = Check Test Case Teardown With Sleep After Test Timeout
- Check Log Message ${tc.teardown.kws[1].msgs[0]} Teardown executed
+ Check Log Message ${tc.teardown[1, 0]} Teardown executed
Failing Teardown After Test Timeout
Check Test Case ${TEST NAME}
@@ -98,7 +98,7 @@ Failing Teardown After Test Timeout
Test Timeout During Teardown
[Documentation] Test timeout should not interrupt teardown but test should be failed afterwards
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.teardown.kws[1].msgs[0]} Teardown executed
+ Check Log Message ${tc.teardown[1, 0]} Teardown executed
Timeouted Setup Passes
Check Test Case ${TEST NAME}
@@ -133,8 +133,8 @@ Keyword Timeout Should Not Be Active For Run Keyword Variants But To Keywords Th
Logging With Timeouts
[Documentation] Testing that logging works with timeouts
${tc} = Check Test Case Timeouted Keyword Passes
- Check Log Message ${tc.kws[0].msgs[1]} Testing logging in timeouted test
- Check Log Message ${tc.kws[1].kws[0].msgs[1]} Testing logging in timeouted keyword
+ Check Log Message ${tc[0, 1]} Testing logging in timeouted test
+ Check Log Message ${tc[1, 0, 1]} Testing logging in timeouted keyword
Timeouted Keyword Called With Wrong Number of Arguments
Check Test Case ${TEST NAME}
@@ -144,31 +144,31 @@ Timeouted Keyword Called With Wrong Number of Arguments with Run Keyword
Test Timeout Logging
${tc} = Check Test Case Passing
- Timeout should have been active ${tc.kws[0]} 1 second 1
+ Timeout should have been active ${tc[0]} 1 second 1
${tc} = Check Test Case Failing Before Timeout
- Timeout should have been active ${tc.kws[0]} 2 seconds 3
+ Timeout should have been active ${tc[0]} 2 seconds 3
${tc} = Check Test Case Sleeping And Timeouting
- Timeout should have been active ${tc.kws[0]} 1 second 2 exceeded=True
+ Timeout should have been active ${tc[0]} 1 second 2 exceeded=True
Keyword Timeout Logging
${tc} = Check Test Case Timeouted Keyword Passes
- Keyword timeout should have been active ${tc.kws[1].kws[0]} 5 seconds 2
+ Keyword timeout should have been active ${tc[1, 0]} 5 seconds 2
${tc} = Check Test Case Timeouted Keyword Fails Before Timeout
- Keyword timeout should have been active ${tc.kws[0].kws[0]} 2 hours 30 minutes 3
+ Keyword timeout should have been active ${tc[0, 0]} 2 hours 30 minutes 3
${tc} = Check Test Case Timeouted Keyword Timeouts
- Keyword timeout should have been active ${tc.kws[0].kws[0]} 99 milliseconds 2 exceeded=True
+ Keyword timeout should have been active ${tc[0, 0]} 99 milliseconds 2 exceeded=True
Zero timeout is ignored
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.timeout} 0 seconds
- Should Be Equal ${tc.kws[0].timeout} 0 seconds
- Elapsed Time Should Be Valid ${tc.kws[0].elapsed_time} minimum=0.099
+ Should Be Equal ${tc.timeout} ${None}
+ Should Be Equal ${tc[0].timeout} ${None}
+ Elapsed Time Should Be Valid ${tc[0].elapsed_time} minimum=0.099
Negative timeout is ignored
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.kws[0].timeout} - 1 second
- Should Be Equal ${tc.kws[0].timeout} - 1 second
- Elapsed Time Should Be Valid ${tc.kws[0].elapsed_time} minimum=0.099
+ Should Be Equal ${tc.timeout} ${None}
+ Should Be Equal ${tc[0].timeout} ${None}
+ Elapsed Time Should Be Valid ${tc[0].elapsed_time} minimum=0.099
Invalid test timeout
Check Test Case ${TEST NAME}
@@ -179,8 +179,8 @@ Invalid keyword timeout
*** Keywords ***
Timeout should have been active
[Arguments] ${kw} ${timeout} ${msg count} ${exceeded}=False ${type}=Test
- Check Log Message ${kw.msgs[0]} ${type} timeout ${timeout} active. * left. DEBUG pattern=True
- Length Should Be ${kw.msgs} ${msg count}
+ Check Log Message ${kw[0]} ${type} timeout ${timeout} active. * left. DEBUG pattern=True
+ Length Should Be ${kw.body} ${msg count}
IF ${exceeded} Timeout should have exceeded ${kw} ${timeout} ${type}
Keyword timeout should have been active
@@ -189,4 +189,4 @@ Keyword timeout should have been active
Timeout should have exceeded
[Arguments] ${kw} ${timeout} ${type}=Test
- Check Log Message ${kw.msgs[1]} ${type} timeout ${timeout} exceeded. FAIL
+ Check Log Message ${kw[1]} ${type} timeout ${timeout} exceeded. FAIL
diff --git a/atest/robot/running/try_except/try_except.robot b/atest/robot/running/try_except/try_except.robot
index 1b599b5c98f..8d074cd6446 100644
--- a/atest/robot/running/try_except/try_except.robot
+++ b/atest/robot/running/try_except/try_except.robot
@@ -40,16 +40,16 @@ Syntax errors cannot be caught
Finally block executed when no failures
[Template] None
${tc}= Verify try except and block statuses PASS NOT RUN PASS PASS
- Check Log Message ${tc.body[0].body[0].body[0].msgs[0]} all good
- Check Log Message ${tc.body[0].body[2].body[0].msgs[0]} in the else
- Check Log Message ${tc.body[0].body[3].body[0].msgs[0]} Hello from finally!
+ Check Log Message ${tc[0, 0, 0, 0]} all good
+ Check Log Message ${tc[0, 2, 0, 0]} in the else
+ Check Log Message ${tc[0, 3, 0, 0]} Hello from finally!
Finally block executed after catch
[Template] None
${tc}= Verify try except and block statuses FAIL PASS PASS
- Check Log Message ${tc.body[0].body[0].body[0].msgs[0]} all not good FAIL
- Check Log Message ${tc.body[0].body[1].body[0].msgs[0]} we are safe now
- Check Log Message ${tc.body[0].body[2].body[0].msgs[0]} Hello from finally!
+ Check Log Message ${tc[0, 0, 0, 0]} all not good FAIL
+ Check Log Message ${tc[0, 1, 0, 0]} we are safe now
+ Check Log Message ${tc[0, 2, 0, 0]} Hello from finally!
Finally block executed after failure in except
FAIL FAIL NOT RUN PASS
diff --git a/atest/robot/running/while/invalid_while.robot b/atest/robot/running/while/invalid_while.robot
index b0ec2f3a5aa..e2526405943 100644
--- a/atest/robot/running/while/invalid_while.robot
+++ b/atest/robot/running/while/invalid_while.robot
@@ -5,7 +5,7 @@ Suite Setup Run Tests --log test_result_model_as_well running/while/
*** Test Cases ***
Multiple conditions
${tc} = Check Invalid WHILE Test Case
- Should Be Equal ${tc.body[0].condition} Too, many, conditions, !
+ Should Be Equal ${tc[0].condition} Too, many, conditions, !
Invalid condition
Check Invalid WHILE Test Case
@@ -37,16 +37,25 @@ Invalid condition causes normal error
Non-existing variable in condition causes normal error
Check Test Case ${TEST NAME}
+Templatest are not supported
+ ${tc} = Check Test Case ${TEST NAME}
+ Should Be Equal ${tc[0].type} WHILE
+ Should Be Equal ${tc[0].status} FAIL
+ Should Be Equal ${tc[0, 0].type} ITERATION
+ Should Be Equal ${tc[0, 0].status} NOT RUN
+ Check Keyword Data ${tc[0, 0, 0]} ${EMPTY} args=1 status=NOT RUN
+ Check Keyword Data ${tc[0, 0, 1]} ${EMPTY} args=2 status=NOT RUN
+
*** Keywords ***
Check Invalid WHILE Test Case
[Arguments] ${body}=True
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.body[0].type} WHILE
- Should Be Equal ${tc.body[0].status} FAIL
- Should Be Equal ${tc.body[0].body[0].type} ITERATION
- Should Be Equal ${tc.body[0].body[0].status} NOT RUN
+ Should Be Equal ${tc[0].type} WHILE
+ Should Be Equal ${tc[0].status} FAIL
+ Should Be Equal ${tc[0, 0].type} ITERATION
+ Should Be Equal ${tc[0, 0].status} NOT RUN
IF ${body}
- Should Be Equal ${tc.body[0].body[0].body[0].full_name} BuiltIn.Fail
- Should Be Equal ${tc.body[0].body[0].body[0].status} NOT RUN
+ Should Be Equal ${tc[0, 0, 0].full_name} BuiltIn.Fail
+ Should Be Equal ${tc[0, 0, 0].status} NOT RUN
END
RETURN ${tc}
diff --git a/atest/robot/running/while/nested_while.robot b/atest/robot/running/while/nested_while.robot
index 96e08c15726..e745c002619 100644
--- a/atest/robot/running/while/nested_while.robot
+++ b/atest/robot/running/while/nested_while.robot
@@ -5,24 +5,24 @@ Suite Setup Run Tests ${EMPTY} running/while/nested_while.robot
*** Test Cases ***
Inside FOR
${tc}= Check test case ${TEST NAME}
- Check loop attributes ${tc.body[0].body[0].body[0]} PASS 4
- Check loop attributes ${tc.body[0].body[1].body[0]} PASS 3
- Check loop attributes ${tc.body[0].body[2].body[0]} PASS 2
- Length should be ${tc.body[0].body} 3
+ Check loop attributes ${tc[0, 0, 0]} PASS 4
+ Check loop attributes ${tc[0, 1, 0]} PASS 3
+ Check loop attributes ${tc[0, 2, 0]} PASS 2
+ Length should be ${tc[0].body} 3
Failing inside FOR
${tc}= Check test case ${TEST NAME}
- Check loop attributes ${tc.body[0].body[0].body[0]} FAIL 2
- Length should be ${tc.body[0].body} 1
+ Check loop attributes ${tc[0, 0, 0]} FAIL 2
+ Length should be ${tc[0].body} 1
Inside IF
${tc}= Check test case ${TEST NAME}
- Check loop attributes ${tc.body[0].body[0].body[1]} PASS 4
+ Check loop attributes ${tc[0, 0, 1]} PASS 4
In suite setup
${suite}= Get Test Suite Nested While
- Check loop attributes ${suite.setup.body[1]} PASS 4
+ Check loop attributes ${suite.setup[1]} PASS 4
In suite teardown
${suite}= Get Test Suite Nested While
- Check loop attributes ${suite.teardown.body[1]} PASS 4
+ Check loop attributes ${suite.teardown[1]} PASS 4
diff --git a/atest/robot/running/while/on_limit.robot b/atest/robot/running/while/on_limit.robot
index 6d69d51bc65..91415d27cb9 100644
--- a/atest/robot/running/while/on_limit.robot
+++ b/atest/robot/running/while/on_limit.robot
@@ -4,58 +4,56 @@ Resource while.resource
*** Test Cases ***
On limit pass with time limit defined
- Check Test Case ${TESTNAME}
+ Check WHILE Loop PASS not known
On limit pass with iteration limit defined
Check WHILE loop PASS 5
-On limit message without limit
- Check Test Case ${TESTNAME}
-
On limit fail
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL 5
On limit pass with failures in loop
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL 1
On limit pass with continuable failure
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL 2
On limit fail with continuable failure
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL 2
Invalid on_limit
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL 1 not_run=True
Invalid on_limit from variable
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL 1 not_run=True
-On limit without limit defined
- Check Test Case ${TESTNAME}
+On limit without limit
+ Check WHILE Loop FAIL 1 not_run=True
On limit with invalid variable
- Check Test Case ${TESTNAME}
-
-Wrong WHILE argument
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL 1 not_run=True
On limit message
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL 11
+
+On limit message without limit
+ Check WHILE Loop FAIL 10000
On limit message from variable
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL 5
Part of on limit message from variable
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL 5
-No on limit message
- Check Test Case ${TESTNAME}
+On limit message is not used if limit is not hit
+ Check WHILE Loop PASS 2
Nested while on limit message
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL 1 path=body[0]
+ Check WHILE Loop FAIL 5 path=body[0].body[0].body[0]
On limit message before limit
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL 5
On limit message with invalid variable
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL 1 not_run=True
diff --git a/atest/robot/running/while/while.resource b/atest/robot/running/while/while.resource
index f0c49be600f..392399f1bb6 100644
--- a/atest/robot/running/while/while.resource
+++ b/atest/robot/running/while/while.resource
@@ -3,14 +3,19 @@ Resource atest_resource.robot
*** Keywords ***
Check WHILE loop
- [Arguments] ${status} ${iterations} ${path}=body[0]
- ${tc}= Check test case ${TEST NAME}
- ${loop}= Check loop attributes ${tc.${path}} ${status} ${iterations}
+ [Arguments] ${status} ${iterations} ${path}=body[0] ${not_run}=False
+ ${tc}= Check Test Case ${TEST NAME}
+ ${loop}= Check Loop Attributes ${tc.${path}} ${status} ${iterations}
+ IF ${not_run}
+ Should Be Equal ${loop.body[0].status} NOT RUN
+ END
RETURN ${loop}
-Check loop attributes
+Check Loop Attributes
[Arguments] ${loop} ${status} ${iterations}
- Should be equal ${loop.type} WHILE
- Should be equal ${loop.status} ${status}
- Length Should Be ${loop.kws} ${iterations}
+ Should Be Equal ${loop.type} WHILE
+ Should Be Equal ${loop.status} ${status}
+ IF '${iterations}' != 'not known'
+ Length Should Be ${loop.non_messages} ${iterations}
+ END
RETURN ${loop}
diff --git a/atest/robot/running/while/while.robot b/atest/robot/running/while/while.robot
index 899d6881da6..76580a70e04 100644
--- a/atest/robot/running/while/while.robot
+++ b/atest/robot/running/while/while.robot
@@ -5,7 +5,7 @@ Suite Setup Run Tests ${EMPTY} running/while/while.robot
*** Test Cases ***
Loop executed once
${loop}= Check While Loop PASS 1
- Check Log Message ${loop.body[0].body[0].msgs[0]} 1
+ Check Log Message ${loop[0, 0, 0]} 1
Loop executed multiple times
Check While Loop PASS 5
diff --git a/atest/robot/running/while/while_limit.robot b/atest/robot/running/while/while_limit.robot
index b7260521114..22185673eee 100644
--- a/atest/robot/running/while/while_limit.robot
+++ b/atest/robot/running/while/while_limit.robot
@@ -4,59 +4,63 @@ Resource while.resource
*** Test Cases ***
Default limit is 10000 iterations
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL 10000
Limit with iteration count
- Check while loop FAIL 5
+ Check WHILE Loop FAIL 5
Iteration count with 'times' suffix
- Check while loop FAIL 3
+ Check WHILE Loop FAIL 3
Iteration count with 'x' suffix
- Check while loop FAIL 4
+ Check WHILE Loop FAIL 4
Iteration count normalization
- ${tc}= Check Test Case ${TESTNAME}
- Should Be Equal ${tc.body[0].limit} 1_000
- Should Be Equal ${tc.body[1].limit} 3 0 T i m e S
+ ${loop}= Check WHILE Loop PASS 1 body[0]
+ Should Be Equal ${loop.limit} 1_000
+ ${loop}= Check WHILE Loop FAIL 30 body[1]
+ Should Be Equal ${loop.limit} 3 0 T i m e S
Limit as timestr
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL not known
Limit from variable
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL 11
Part of limit from variable
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL not known
Limit can be disabled
- Check Test Case ${TESTNAME}
+ Check WHILE Loop PASS 10041
-No Condition With Limit
- Check Test Case ${TESTNAME}
+No condition with limit
+ Check WHILE Loop FAIL 2
Limit exceeds in teardown
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL not known teardown.body[0]
Limit exceeds after failures in teardown
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL 2 teardown.body[0]
Continue after limit in teardown
- Check Test Case ${TESTNAME}
+ Check WHILE Loop PASS not known teardown.body[0]
Invalid limit invalid suffix
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL 1 not_run=True
Invalid limit invalid value
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL 1 not_run=True
Invalid limit mistyped prefix
- Check Test Case ${TESTNAME}
+ Check WHILE Loop FAIL 1 not_run=True
+
+Limit with non-existing variable
+ Check WHILE Loop FAIL 1 not_run=True
Limit used multiple times
- ${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.body[0].limit} 2
+ ${loop}= Check WHILE Loop FAIL 1 not_run=True
+ Should Be Equal ${loop.limit} 2
Invalid values after limit
- ${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.body[0].condition} $variable < 2, limit=2, invalid
+ ${loop}= Check WHILE Loop FAIL 1 not_run=True
+ Should Be Equal ${loop.condition} $variable < 2, limit=2, invalid
diff --git a/atest/robot/standard_libraries/builtin/call_method.robot b/atest/robot/standard_libraries/builtin/call_method.robot
index 86e02487518..8e34ce4d981 100644
--- a/atest/robot/standard_libraries/builtin/call_method.robot
+++ b/atest/robot/standard_libraries/builtin/call_method.robot
@@ -4,32 +4,32 @@ Resource atest_resource.robot
*** Test Cases ***
Call Method
- Check Test Case ${TEST NAME}
+ Check Test Case ${TEST NAME}
Call Method Returns
- Check Test Case ${TEST NAME}
+ Check Test Case ${TEST NAME}
Called Method Fails
- ${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.body[0].msgs[0]} Calling method 'my_method' failed: Expected failure FAIL
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Log Message ${tc[0, 0]} Calling method 'my_method' failed: Expected failure FAIL
${error} = Catenate SEPARATOR=\n
... RuntimeError: Expected failure
...
... The above exception was the direct cause of the following exception:
...
... RuntimeError: Calling method 'my_method' failed: Expected failure
- Traceback Should Be ${tc.body[0].msgs[1]}
- ... standard_libraries/builtin/objects_for_call_method.py my_method raise RuntimeError('Expected failure')
+ Traceback Should Be ${tc[0, 1]}
+ ... standard_libraries/builtin/objects_for_call_method.py my_method raise RuntimeError("Expected failure")
... error=${error}
Call Method With Kwargs
- Check Test Case ${TEST NAME}
+ Check Test Case ${TEST NAME}
Equals in non-kwargs must be escaped
- Check Test Case ${TEST NAME}
+ Check Test Case ${TEST NAME}
Call Method From Module
- Check Test Case ${TEST NAME}
+ Check Test Case ${TEST NAME}
Call Non Existing Method
- Check Test Case ${TEST NAME}
+ Check Test Case ${TEST NAME}
diff --git a/atest/robot/standard_libraries/builtin/converter.robot b/atest/robot/standard_libraries/builtin/converter.robot
index 632e676d759..4911659cec2 100644
--- a/atest/robot/standard_libraries/builtin/converter.robot
+++ b/atest/robot/standard_libraries/builtin/converter.robot
@@ -5,7 +5,7 @@ Resource atest_resource.robot
*** Test Cases ***
Convert To Integer
${tc}= Check Test Case ${TEST NAME}
- Verify argument type message ${tc.kws[0].kws[0].msgs[0]}
+ Verify argument type message ${tc[0, 0, 0]}
Convert To Integer With Base
Check Test Case ${TEST NAME}
@@ -18,19 +18,19 @@ Convert To Integer With Embedded Base
Convert To Binary
${tc}= Check Test Case ${TEST NAME}
- Verify argument type message ${tc.kws[0].kws[0].msgs[0]}
+ Verify argument type message ${tc[0, 0, 0]}
Convert To Octal
${tc}= Check Test Case ${TEST NAME}
- Verify argument type message ${tc.kws[0].kws[0].msgs[0]}
+ Verify argument type message ${tc[0, 0, 0]}
Convert To Hex
${tc}= Check Test Case ${TEST NAME}
- Verify argument type message ${tc.kws[0].kws[0].msgs[0]}
+ Verify argument type message ${tc[0, 0, 0]}
Convert To Number
${tc}= Check Test Case ${TEST NAME}
- Verify argument type message ${tc.kws[0].kws[0].msgs[0]}
+ Verify argument type message ${tc[0, 0, 0]}
Convert To Number With Precision
Check Test Case ${TEST NAME}
@@ -40,11 +40,11 @@ Numeric conversions with long types
Convert To String
${tc}= Check Test Case ${TEST NAME}
- Verify argument type message ${tc.kws[0].msgs[0]}
+ Verify argument type message ${tc[0, 0]}
Convert To Boolean
${tc}= Check Test Case ${TEST NAME}
- Verify argument type message ${tc.kws[0].msgs[0]}
+ Verify argument type message ${tc[0, 0]}
Create List
Check Test Case ${TEST NAME}
diff --git a/atest/robot/standard_libraries/builtin/count.robot b/atest/robot/standard_libraries/builtin/count.robot
index 91acc6c8fe5..43122824093 100644
--- a/atest/robot/standard_libraries/builtin/count.robot
+++ b/atest/robot/standard_libraries/builtin/count.robot
@@ -6,24 +6,24 @@ Resource builtin_resource.robot
Get Count
[Documentation] Tested also by Should Contain X Times keyword that uses this intenally.
${tc} = Check test case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} Item found from container 2 times.
- Check Log Message ${tc.kws[1].kws[0].msgs[0]} Item found from container 2 times.
- Check Log Message ${tc.kws[2].kws[0].msgs[0]} Item found from container 1 time.
- Check Log Message ${tc.kws[3].kws[0].msgs[0]} Item found from container 1 time.
- Check Log Message ${tc.kws[4].kws[0].msgs[0]} Item found from container 50 times.
- Check Log Message ${tc.kws[5].kws[0].msgs[0]} Item found from container 0 times.
+ Check Log Message ${tc[0, 0, 0]} Item found from container 2 times.
+ Check Log Message ${tc[1, 0, 0]} Item found from container 2 times.
+ Check Log Message ${tc[2, 0, 0]} Item found from container 1 time.
+ Check Log Message ${tc[3, 0, 0]} Item found from container 1 time.
+ Check Log Message ${tc[4, 0, 0]} Item found from container 50 times.
+ Check Log Message ${tc[5, 0, 0]} Item found from container 0 times.
Should Contain X Times with strings
${tc} = Check test case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} Item found from container 2 times.
- Check Log Message ${tc.kws[1].msgs[0]} Item found from container 1 time.
- Check Log Message ${tc.kws[3].msgs[0]} Item found from container 0 times.
+ Check Log Message ${tc[0, 0]} Item found from container 2 times.
+ Check Log Message ${tc[1, 0]} Item found from container 1 time.
+ Check Log Message ${tc[3, 0]} Item found from container 0 times.
Should Contain X Times with containers
${tc} = Check test case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} Item found from container 1 time.
- Check Log Message ${tc.kws[1].msgs[0]} Item found from container 2 times.
- Check Log Message ${tc.kws[3].msgs[0]} Item found from container 0 times.
+ Check Log Message ${tc[0, 0]} Item found from container 1 time.
+ Check Log Message ${tc[1, 0]} Item found from container 2 times.
+ Check Log Message ${tc[3, 0]} Item found from container 0 times.
Should Contain X Times failing
Check test case ${TESTNAME}
diff --git a/atest/robot/standard_libraries/builtin/evaluate.robot b/atest/robot/standard_libraries/builtin/evaluate.robot
index 7454e955e1e..02696b60849 100644
--- a/atest/robot/standard_libraries/builtin/evaluate.robot
+++ b/atest/robot/standard_libraries/builtin/evaluate.robot
@@ -9,6 +9,9 @@ Resource atest_resource.robot
Evaluate
Check Test Case ${TESTNAME}
+Custom additions to builtins are supported
+ Check Test Case ${TESTNAME}
+
Modules are imported automatically
Check Test Case ${TESTNAME}
diff --git a/atest/robot/standard_libraries/builtin/fail.robot b/atest/robot/standard_libraries/builtin/fail.robot
index 13e54fa4761..c78ede1bbe6 100644
--- a/atest/robot/standard_libraries/builtin/fail.robot
+++ b/atest/robot/standard_libraries/builtin/fail.robot
@@ -5,11 +5,11 @@ Resource atest_resource.robot
*** Test Cases ***
Fail
${tc}= Check Test Tags ${TESTNAME} force1 force2
- Length Should Be ${tc.kws[0].msgs} 1
+ Length Should Be ${tc[0].body} 1
Fail with message
${tc}= Check Test Tags ${TESTNAME} force1 force2
- Length Should Be ${tc.kws[0].msgs} 1
+ Length Should Be ${tc[0].body} 1
Fail with non-string message
Check Test Case ${TESTNAME}
@@ -19,37 +19,37 @@ Fail with non-true message having non-empty string representation
Set one tag
${tc}= Check Test Tags ${TESTNAME} force1 force2 tag
- Length Should Be ${tc.kws[0].msgs} 2
- Check Log Message ${tc.kws[0].msgs[0]} Set tag 'tag'.
+ Length Should Be ${tc[0].body} 2
+ Check Log Message ${tc[0, 0]} Set tag 'tag'.
Set multiple tags
${tc}= Check Test Tags ${TESTNAME} force1 force2 tag1 tag2
- Length Should Be ${tc.kws[0].msgs} 2
- Check Log Message ${tc.kws[0].msgs[0]} Set tags 'tag1' and 'tag2'.
+ Length Should Be ${tc[0].body} 2
+ Check Log Message ${tc[0, 0]} Set tags 'tag1' and 'tag2'.
Remove one tag
${tc}= Check Test Tags ${TESTNAME} force2
- Length Should Be ${tc.kws[0].msgs} 2
- Check Log Message ${tc.kws[0].msgs[0]} Removed tag 'force1'.
+ Length Should Be ${tc[0].body} 2
+ Check Log Message ${tc[0, 0]} Removed tag 'force1'.
Remove multiple tags
${tc}= Check Test Tags ${TESTNAME}
- Length Should Be ${tc.kws[0].msgs} 2
- Check Log Message ${tc.kws[0].msgs[0]} Removed tags 'force1' and 'force2'.
+ Length Should Be ${tc[0].body} 2
+ Check Log Message ${tc[0, 0]} Removed tags 'force1' and 'force2'.
Remove multiple tags with pattern
${tc}= Check Test Tags ${TESTNAME}
- Length Should Be ${tc.kws[0].msgs} 2
- Check Log Message ${tc.kws[0].msgs[0]} Removed tag 'force?'.
+ Length Should Be ${tc[0].body} 2
+ Check Log Message ${tc[0, 0]} Removed tag 'force?'.
Set and remove tags
${tc}= Check Test Tags ${TESTNAME} force2 tag1 tag2
- Length Should Be ${tc.kws[0].msgs} 3
- Check Log Message ${tc.kws[0].msgs[0]} Removed tags 'force1' and 'nonEx'.
- Check Log Message ${tc.kws[0].msgs[1]} Set tags 'tag1' and 'tag2'.
+ Length Should Be ${tc[0].body} 3
+ Check Log Message ${tc[0, 0]} Removed tags 'force1' and 'nonEx'.
+ Check Log Message ${tc[0, 1]} Set tags 'tag1' and 'tag2'.
Set tags should not be removed
${tc}= Check Test Tags ${TESTNAME} fii foo
- Length Should Be ${tc.kws[0].msgs} 3
- Check Log Message ${tc.kws[0].msgs[0]} Removed tag 'f*'.
- Check Log Message ${tc.kws[0].msgs[1]} Set tags 'foo' and 'fii'.
+ Length Should Be ${tc[0].body} 3
+ Check Log Message ${tc[0, 0]} Removed tag 'f*'.
+ Check Log Message ${tc[0, 1]} Set tags 'foo' and 'fii'.
diff --git a/atest/robot/standard_libraries/builtin/fatal_error.robot b/atest/robot/standard_libraries/builtin/fatal_error.robot
index a840ddb8f20..009dcd947f3 100644
--- a/atest/robot/standard_libraries/builtin/fatal_error.robot
+++ b/atest/robot/standard_libraries/builtin/fatal_error.robot
@@ -10,4 +10,4 @@ Subsequent tests are not executed after `Fatal Error` keyword has been used
Check Test Case ${TESTNAME}
Suite teardown is executed after `Fatal Error` keyword
- Check Log Message ${SUITE.teardown.msgs[0]} AssertionError FAIL
+ Check Log Message ${SUITE.teardown[0]} AssertionError FAIL
diff --git a/atest/robot/standard_libraries/builtin/length.robot b/atest/robot/standard_libraries/builtin/length.robot
index 4c111a8fd9b..7825b0c8ff7 100644
--- a/atest/robot/standard_libraries/builtin/length.robot
+++ b/atest/robot/standard_libraries/builtin/length.robot
@@ -5,19 +5,19 @@ Resource builtin_resource.robot
*** Test Cases ***
Get Length
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} Length is 0.
- Check Log Message ${tc.kws[1].kws[0].msgs[0]} Length is 1.
- Check Log Message ${tc.kws[2].kws[0].msgs[0]} Length is 2.
- Check Log Message ${tc.kws[3].kws[0].msgs[0]} Length is 3.
- Check Log Message ${tc.kws[4].kws[0].msgs[0]} Length is 11.
- Check Log Message ${tc.kws[5].kws[0].msgs[0]} Length is 0.
+ Check Log Message ${tc[0, 0, 0]} Length is 0.
+ Check Log Message ${tc[1, 0, 0]} Length is 1.
+ Check Log Message ${tc[2, 0, 0]} Length is 2.
+ Check Log Message ${tc[3, 0, 0]} Length is 3.
+ Check Log Message ${tc[4, 0, 0]} Length is 11.
+ Check Log Message ${tc[5, 0, 0]} Length is 0.
Length Should Be
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[-1].msgs[0]} Length is 2.
- Check Log Message ${tc.kws[-1].msgs[1]} Length of '*' should be 3 but is 2. FAIL pattern=yep
- Check Log Message ${tc.kws[-1].msgs[2]} Traceback* DEBUG pattern=yep
- Length Should Be ${tc.kws[-1].msgs} 3
+ Check Log Message ${tc[-1, 0]} Length is 2.
+ Check Log Message ${tc[-1, 1]} Length of '*' should be 3 but is 2. FAIL pattern=yep
+ Check Log Message ${tc[-1, 2]} Traceback* DEBUG pattern=yep
+ Length Should Be ${tc[-1].body} 3
Length Should Be with custom message
Check Test Case ${TESTNAME}
@@ -26,25 +26,25 @@ Length Should Be with invalid length
Check Test Case ${TESTNAME}
Should Be Empty
- Check test case ${TESTNAME} 1
- Check test case ${TESTNAME} 2
- Check test case ${TESTNAME} 3
+ Check Test Case ${TESTNAME} 1
+ Check Test Case ${TESTNAME} 2
+ Check Test Case ${TESTNAME} 3
Should Be Empty with custom message
- Check test case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Should Not Be Empty
- Check test case ${TESTNAME} 1
- Check test case ${TESTNAME} 2
+ Check Test Case ${TESTNAME} 1
+ Check Test Case ${TESTNAME} 2
Should Not Be Empty with custom message
- Check test case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Getting length with `length` method
- Check test case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Getting length with `size` method
- Check test case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Getting length with `length` attribute
- Check test case ${TESTNAME}
+ Check Test Case ${TESTNAME}
diff --git a/atest/robot/standard_libraries/builtin/listener_printing_start_end_kw.py b/atest/robot/standard_libraries/builtin/listener_printing_start_end_kw.py
index 9a91451a5bc..a4431f0123e 100644
--- a/atest/robot/standard_libraries/builtin/listener_printing_start_end_kw.py
+++ b/atest/robot/standard_libraries/builtin/listener_printing_start_end_kw.py
@@ -1,14 +1,13 @@
import sys
-
ROBOT_LISTENER_API_VERSION = 2
def start_keyword(name, attrs):
- sys.stdout.write('start keyword %s\n' % name)
- sys.stderr.write('start keyword %s\n' % name)
+ sys.stdout.write(f"start keyword {name}\n")
+ sys.stderr.write(f"start keyword {name}\n")
def end_keyword(name, attrs):
- sys.stdout.write('end keyword %s\n' % name)
- sys.stderr.write('end keyword %s\n' % name)
+ sys.stdout.write(f"end keyword {name}\n")
+ sys.stderr.write(f"end keyword {name}\n")
diff --git a/atest/robot/standard_libraries/builtin/listener_using_builtin.py b/atest/robot/standard_libraries/builtin/listener_using_builtin.py
index 07b83c0001c..22fe1ba767d 100644
--- a/atest/robot/standard_libraries/builtin/listener_using_builtin.py
+++ b/atest/robot/standard_libraries/builtin/listener_using_builtin.py
@@ -5,5 +5,5 @@
def start_keyword(*args):
- if BIN.get_variables()['${TESTNAME}'] == 'Listener Using BuiltIn':
- BIN.set_test_variable('${SET BY LISTENER}', 'quux')
+ if BIN.get_variables()["${TESTNAME}"] == "Listener Using BuiltIn":
+ BIN.set_test_variable("${SET BY LISTENER}", "quux")
diff --git a/atest/robot/standard_libraries/builtin/log.robot b/atest/robot/standard_libraries/builtin/log.robot
index 46867ea77b8..d714149f292 100644
--- a/atest/robot/standard_libraries/builtin/log.robot
+++ b/atest/robot/standard_libraries/builtin/log.robot
@@ -8,187 +8,191 @@ ${HTML} Robot Framework
*** Test Cases ***
Log
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} Hello, world!
- Check Log Message ${tc.kws[1].msgs[0]} 42
- Check Log Message ${tc.kws[2].msgs[0]} None
- Check Log Message ${tc.kws[3].msgs[0]} String presentation of MyObject
+ Check Log Message ${tc[0, 0]} Hello, world!
+ Check Log Message ${tc[1, 0]} 42
+ Check Log Message ${tc[2, 0]} None
+ Check Log Message ${tc[3, 0]} String presentation of MyObject
Log with different levels
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[1]} Log says: Hello from tests! INFO
- Check Log Message ${tc.kws[1].msgs[1]} Trace level TRACE
- Check Log Message ${tc.kws[2].msgs[1]} Debug level DEBUG
- Check Log Message ${tc.kws[3].msgs[1]} Info level INFO
- Check Log Message ${tc.kws[4].msgs[1]} Warn level WARN
- Check Log Message ${tc.kws[5].msgs[1]} Error level ERROR
- Check Log Message ${ERRORS[0]} Warn level WARN
- Check Log Message ${ERRORS[1]} Error level ERROR
- Length Should Be ${ERRORS} 4 # Two deprecation warnings from `repr`.
+ Check Log Message ${tc[0, 1]} Log says: Hello from tests! INFO
+ Check Log Message ${tc[1, 1]} Trace level TRACE
+ Check Log Message ${tc[2, 1]} Debug level DEBUG
+ Check Log Message ${tc[3, 1]} Info level INFO
+ Check Log Message ${tc[4, 1]} Warn level WARN
+ Check Log Message ${tc[5, 1]} Error level ERROR
+ Check Log Message ${ERRORS[0]} Warn level WARN
+ Check Log Message ${ERRORS[1]} Error level ERROR
+ Length Should Be ${ERRORS} 4 # Two deprecation warnings from `repr`.
Invalid log level failure is catchable
Check Test Case ${TEST NAME}
HTML is escaped by default
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} not bold
- Check Log Message ${tc.kws[1].msgs[0]} ${HTML}
+ Check Log Message ${tc[0, 0]} not bold
+ Check Log Message ${tc[1, 0]} ${HTML}
HTML pseudo level
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} bold html=True
- Check Log Message ${tc.kws[1].msgs[0]} ${HTML} html=True
+ Check Log Message ${tc[0, 0]} bold html=True
+ Check Log Message ${tc[1, 0]} ${HTML} html=True
Explicit HTML
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} bold html=True
- Check Log Message ${tc.kws[1].msgs[0]} ${HTML} DEBUG html=True
- Check Log Message ${tc.kws[2].msgs[0]} ${HTML} DEBUG
+ Check Log Message ${tc[0, 0]} bold html=True
+ Check Log Message ${tc[1, 0]} ${HTML} DEBUG html=True
+ Check Log Message ${tc[2, 0]} ${HTML} DEBUG
FAIL is not valid log level
Check Test Case ${TEST NAME}
Log also to console
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} Hello, console!
- Check Log Message ${tc.kws[1].msgs[0]} ${HTML} DEBUG html=True
+ Check Log Message ${tc[0, 0]} Hello, console!
+ Check Log Message ${tc[1, 0]} ${HTML} DEBUG html=True
Stdout Should Contain Hello, console!\n
Stdout Should Contain ${HTML}\n
CONSOLE pseudo level
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} Hello, info and console!
+ Check Log Message ${tc[0, 0]} Hello, info and console!
Stdout Should Contain Hello, info and console!\n
repr=True
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} The 'repr' argument of 'BuiltIn.Log' is deprecated. Use 'formatter=repr' instead. WARN
- Check Log Message ${tc.kws[0].msgs[1]} Nothing special here
- Check Log Message ${tc.kws[1].msgs[0]} The 'repr' argument of 'BuiltIn.Log' is deprecated. Use 'formatter=repr' instead. WARN
- Check Log Message ${tc.kws[1].msgs[1]} 'Hyvää yötä ☃!'
+ Check Log Message ${tc[0, 0]} The 'repr' argument of 'BuiltIn.Log' is deprecated. Use 'formatter=repr' instead. WARN
+ Check Log Message ${tc[0, 1]} Nothing special here
+ Check Log Message ${tc[1, 0]} The 'repr' argument of 'BuiltIn.Log' is deprecated. Use 'formatter=repr' instead. WARN
+ Check Log Message ${tc[1, 1]} 'Hyvää yötä ☃!'
formatter=repr
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} 'Nothing special here'
- Check Log Message ${tc.kws[1].msgs[0]} 'Hyvää yötä ☃!'
- Check Log Message ${tc.kws[2].msgs[0]} 42 DEBUG
- Check Log Message ${tc.kws[4].msgs[0]} b'\\x00abc\\xff (formatter=repr)'
- Check Log Message ${tc.kws[6].msgs[0]} 'hyvä'
+ Check Log Message ${tc[0, 0]} 'Nothing special here'
+ Check Log Message ${tc[1, 0]} 'Hyvää yötä ☃!'
+ Check Log Message ${tc[2, 0]} 42 DEBUG
+ Check Log Message ${tc[4, 0]} b'\\x00abc\\xff (formatter=repr)'
+ Check Log Message ${tc[6, 0]} 'hyvä'
Stdout Should Contain b'\\x00abc\\xff (formatter=repr)'
formatter=ascii
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} 'Nothing special here'
- Check Log Message ${tc.kws[1].msgs[0]} 'Hyv\\xe4\\xe4 y\\xf6t\\xe4 \\u2603!'
- Check Log Message ${tc.kws[2].msgs[0]} 42 DEBUG
- Check Log Message ${tc.kws[4].msgs[0]} b'\\x00abc\\xff (formatter=ascii)'
- Check Log Message ${tc.kws[6].msgs[0]} 'hyva\\u0308'
+ Check Log Message ${tc[0, 0]} 'Nothing special here'
+ Check Log Message ${tc[1, 0]} 'Hyv\\xe4\\xe4 y\\xf6t\\xe4 \\u2603!'
+ Check Log Message ${tc[2, 0]} 42 DEBUG
+ Check Log Message ${tc[4, 0]} b'\\x00abc\\xff (formatter=ascii)'
+ Check Log Message ${tc[6, 0]} 'hyva\\u0308'
Stdout Should Contain b'\\x00abc\\xff (formatter=ascii)'
formatter=str
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} Nothing special here
- Check Log Message ${tc.kws[1].msgs[0]} Hyvää yötä ☃!
- Check Log Message ${tc.kws[2].msgs[0]} 42 DEBUG
- Check Log Message ${tc.kws[4].msgs[0]} abc\\xff (formatter=str)
- Check Log Message ${tc.kws[6].msgs[0]} hyvä
- Stdout Should Contain abc\\xff (formatter=str)
+ Check Log Message ${tc[0, 0]} Nothing special here
+ Check Log Message ${tc[1, 0]} Hyvää yötä ☃!
+ Check Log Message ${tc[2, 0]} 42 DEBUG
+ Check Log Message ${tc[4, 0]} abc\xff (formatter=str)
+ Check Log Message ${tc[6, 0]} hyvä
+ Stdout Should Contain abc\xff (formatter=str)
formatter=repr pretty prints
${tc} = Check Test Case ${TEST NAME}
${long string} = Evaluate ' '.join(['Robot Framework'] * 1000)
${small dict} = Set Variable {'small': 'dict', 3: b'items', 'NOT': 'sorted'}
${small list} = Set Variable ['small', b'list', 'not sorted', 4]
- Check Log Message ${tc.kws[1].msgs[0]} '${long string}'
- Check Log Message ${tc.kws[3].msgs[0]} ${small dict}
- Check Log Message ${tc.kws[5].msgs[0]} {'big': 'dict',\n 'long': '${long string}',\n 'nested': ${small dict},\n 'list': [1, 2, 3],\n 'sorted': False}
- Check Log Message ${tc.kws[7].msgs[0]} ${small list}
- Check Log Message ${tc.kws[9].msgs[0]} ['big',\n 'list',\n '${long string}',\n b'${long string}',\n ['nested', ('tuple', 2)],\n ${small dict}]
- Check Log Message ${tc.kws[11].msgs[0]} ['hyvä', b'hyv\\xe4', {'☃': b'\\x00\\xff'}]
+ Check Log Message ${tc[1, 0]} '${long string}'
+ Check Log Message ${tc[3, 0]} ${small dict}
+ Check Log Message ${tc[5, 0]} {'big': 'dict',\n 'long': '${long string}',\n 'nested': ${small dict},\n 'list': [1, 2, 3],\n 'sorted': False}
+ Check Log Message ${tc[7, 0]} ${small list}
+ Check Log Message ${tc[9, 0]} ['big',\n 'list',\n '${long string}',\n b'${long string}',\n ['nested', ('tuple', 2)],\n ${small dict}]
+ Check Log Message ${tc[11, 0]} ['hyvä', b'hyv\\xe4', {'☃': b'\\x00\\xff'}]
Stdout Should Contain ${small dict}
Stdout Should Contain ${small list}
formatter=len
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} 20
- Check Log Message ${tc.kws[1].msgs[0]} 13 DEBUG
- Check Log Message ${tc.kws[3].msgs[0]} 21
- Check Log Message ${tc.kws[5].msgs[0]} 5
+ Check Log Message ${tc[0, 0]} 20
+ Check Log Message ${tc[1, 0]} 13 DEBUG
+ Check Log Message ${tc[3, 0]} 21
+ Check Log Message ${tc[5, 0]} 5
formatter=type
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} str
- Check Log Message ${tc.kws[1].msgs[0]} str
- Check Log Message ${tc.kws[2].msgs[0]} int DEBUG
- Check Log Message ${tc.kws[4].msgs[0]} bytes
- Check Log Message ${tc.kws[6].msgs[0]} datetime
+ Check Log Message ${tc[0, 0]} str
+ Check Log Message ${tc[1, 0]} str
+ Check Log Message ${tc[2, 0]} int DEBUG
+ Check Log Message ${tc[4, 0]} bytes
+ Check Log Message ${tc[6, 0]} datetime
formatter=invalid
Check Test Case ${TEST NAME}
Log callable
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} *objects_for_call_method.MyObject* pattern=yes
- Check Log Message ${tc.kws[2].msgs[0]} at *> pattern=yes
+ Check Log Message ${tc[0, 0]} *objects_for_call_method.MyObject* pattern=yes
+ Check Log Message ${tc[2, 0]} at *> pattern=yes
Log Many
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} Log Many says:
- Check Log Message ${tc.kws[0].msgs[1]} 1
- Check Log Message ${tc.kws[0].msgs[2]} 2
- Check Log Message ${tc.kws[0].msgs[3]} 3
- Check Log Message ${tc.kws[0].msgs[4]} String presentation of MyObject
- Check Log Message ${tc.kws[1].msgs[0]} Log Many says: Hi!!
- Check Log Message ${tc.kws[2].msgs[0]} 1
- Check Log Message ${tc.kws[2].msgs[1]} 2
- Check Log Message ${tc.kws[2].msgs[2]} 3
- Check Log Message ${tc.kws[2].msgs[3]} String presentation of MyObject
- Should Be Empty ${tc.kws[3].msgs}
- Should Be Empty ${tc.kws[4].msgs}
- Check Log Message ${tc.kws[5].msgs[0]} --
- Check Log Message ${tc.kws[5].msgs[1]} -[]-
- Check Log Message ${tc.kws[5].msgs[2]} -{}-
- Check Log Message ${tc.kws[6].msgs[0]} 1
- Check Log Message ${tc.kws[6].msgs[1]} 2
+ Check Log Message ${tc[0, 0]} Log Many says:
+ Check Log Message ${tc[0, 1]} 1
+ Check Log Message ${tc[0, 2]} 2
+ Check Log Message ${tc[0, 3]} 3
+ Check Log Message ${tc[0, 4]} String presentation of MyObject
+ Check Log Message ${tc[1, 0]} Log Many says: Hi!!
+ Check Log Message ${tc[2, 0]} 1
+ Check Log Message ${tc[2, 1]} 2
+ Check Log Message ${tc[2, 2]} 3
+ Check Log Message ${tc[2, 3]} String presentation of MyObject
+ Should Be Empty ${tc[3].body}
+ Should Be Empty ${tc[4].body}
+ Check Log Message ${tc[5, 0]} preserve
+ Check Log Message ${tc[5, 1]} ${EMPTY}
+ Check Log Message ${tc[5, 2]} empty
+ Check Log Message ${tc[5, 3]} ${EMPTY}
+ Check Log Message ${tc[6, 0]} --
+ Check Log Message ${tc[6, 1]} -[]-
+ Check Log Message ${tc[6, 2]} -{}-
+ Check Log Message ${tc[7, 0]} 1
+ Check Log Message ${tc[7, 1]} 2
Log Many with named and dict arguments
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} a=1
- Check Log Message ${tc.kws[0].msgs[1]} b=2
- Check Log Message ${tc.kws[0].msgs[2]} 3=c
- Check Log Message ${tc.kws[0].msgs[3]} obj=String presentation of MyObject
- Check Log Message ${tc.kws[1].msgs[0]} a=1
- Check Log Message ${tc.kws[1].msgs[1]} b=2
- Check Log Message ${tc.kws[1].msgs[2]} 3=c
- Check Log Message ${tc.kws[1].msgs[3]} obj=String presentation of MyObject
- Check Log Message ${tc.kws[2].msgs[0]} a=1
- Check Log Message ${tc.kws[2].msgs[1]} b=2
- Check Log Message ${tc.kws[2].msgs[2]} 3=c
- Check Log Message ${tc.kws[2].msgs[3]} obj=String presentation of MyObject
- Check Log Message ${tc.kws[2].msgs[4]} b=no override
- Check Log Message ${tc.kws[2].msgs[5]} 3=three
+ Check Log Message ${tc[0, 0]} a=1
+ Check Log Message ${tc[0, 1]} b=2
+ Check Log Message ${tc[0, 2]} 3=c
+ Check Log Message ${tc[0, 3]} obj=String presentation of MyObject
+ Check Log Message ${tc[1, 0]} a=1
+ Check Log Message ${tc[1, 1]} b=2
+ Check Log Message ${tc[1, 2]} 3=c
+ Check Log Message ${tc[1, 3]} obj=String presentation of MyObject
+ Check Log Message ${tc[2, 0]} a=1
+ Check Log Message ${tc[2, 1]} b=2
+ Check Log Message ${tc[2, 2]} 3=c
+ Check Log Message ${tc[2, 3]} obj=String presentation of MyObject
+ Check Log Message ${tc[2, 4]} b=no override
+ Check Log Message ${tc[2, 5]} 3=three
Log Many with positional, named and dict arguments
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} 1
- Check Log Message ${tc.kws[0].msgs[1]} 2
- Check Log Message ${tc.kws[0].msgs[2]} three=3
- Check Log Message ${tc.kws[0].msgs[3]} 4=four
- Check Log Message ${tc.kws[1].msgs[0]} 1
- Check Log Message ${tc.kws[1].msgs[1]} 2
- Check Log Message ${tc.kws[1].msgs[2]} 3
- Check Log Message ${tc.kws[1].msgs[3]} String presentation of MyObject
- Check Log Message ${tc.kws[1].msgs[4]} a=1
- Check Log Message ${tc.kws[1].msgs[5]} b=2
- Check Log Message ${tc.kws[1].msgs[6]} 3=c
- Check Log Message ${tc.kws[1].msgs[7]} obj=String presentation of MyObject
- Check Log Message ${tc.kws[1].msgs[8]} 1
- Check Log Message ${tc.kws[1].msgs[9]} 2
- Check Log Message ${tc.kws[1].msgs[10]} 3
- Check Log Message ${tc.kws[1].msgs[11]} String presentation of MyObject
- Check Log Message ${tc.kws[1].msgs[12]} a=1
- Check Log Message ${tc.kws[1].msgs[13]} b=2
- Check Log Message ${tc.kws[1].msgs[14]} 3=c
- Check Log Message ${tc.kws[1].msgs[15]} obj=String presentation of MyObject
+ Check Log Message ${tc[0, 0]} 1
+ Check Log Message ${tc[0, 1]} 2
+ Check Log Message ${tc[0, 2]} three=3
+ Check Log Message ${tc[0, 3]} 4=four
+ Check Log Message ${tc[1, 0]} 1
+ Check Log Message ${tc[1, 1]} 2
+ Check Log Message ${tc[1, 2]} 3
+ Check Log Message ${tc[1, 3]} String presentation of MyObject
+ Check Log Message ${tc[1, 4]} a=1
+ Check Log Message ${tc[1, 5]} b=2
+ Check Log Message ${tc[1, 6]} 3=c
+ Check Log Message ${tc[1, 7]} obj=String presentation of MyObject
+ Check Log Message ${tc[1, 8]} 1
+ Check Log Message ${tc[1, 9]} 2
+ Check Log Message ${tc[1, 10]} 3
+ Check Log Message ${tc[1, 11]} String presentation of MyObject
+ Check Log Message ${tc[1, 12]} a=1
+ Check Log Message ${tc[1, 13]} b=2
+ Check Log Message ${tc[1, 14]} 3=c
+ Check Log Message ${tc[1, 15]} obj=String presentation of MyObject
Log Many with non-existing variable
Check Test Case ${TEST NAME}
@@ -202,7 +206,7 @@ Log Many with dict variable containing non-dict
Log To Console
${tc} = Check Test Case ${TEST NAME}
FOR ${i} IN RANGE 4
- Should Be Empty ${tc.kws[${i}].msgs}
+ Should Be Empty ${tc[${i}].body}
END
Stdout Should Contain stdout äö w/ newline\n
Stdout Should Contain stdout äö w/o new......line äö
diff --git a/atest/robot/standard_libraries/builtin/log_variables.robot b/atest/robot/standard_libraries/builtin/log_variables.robot
index e1cfe47a2d8..3d58d5affbb 100644
--- a/atest/robot/standard_libraries/builtin/log_variables.robot
+++ b/atest/robot/standard_libraries/builtin/log_variables.robot
@@ -5,7 +5,7 @@ Resource atest_resource.robot
*** Test Cases ***
Log Variables In Suite Setup
- Set Test Variable ${KW} ${SUITE.setup.body[7]}
+ Set Test Variable ${KW} ${SUITE.setup[7]}
Set Test Variable ${INDEX} ${0}
Check Variable Message \${/} = * pattern=yes
Check Variable Message \${:} = ${:}
@@ -24,7 +24,7 @@ Log Variables In Suite Setup
Check Variable Message \${LOG_LEVEL} = INFO
Check Variable Message \${None} = None
Check Variable Message \${null} = None
- Check Variable Message \&{OPTIONS} = { include=[] | exclude=[] | skip=[] | skip_on_failure=[] }
+ Check Variable Message \&{OPTIONS} = { rpa=False | include=[] | exclude=[] | skip=[] | skip_on_failure=[] | console_width=78 }
Check Variable Message \${OUTPUT_DIR} = * pattern=yes
Check Variable Message \${OUTPUT_FILE} = * pattern=yes
Check Variable Message \${PREV_TEST_MESSAGE} =
@@ -48,7 +48,7 @@ Log Variables In Suite Setup
Log Variables In Test
${test} = Check Test Case Log Variables
- Set Test Variable ${KW} ${test.body[0]}
+ Set Test Variable ${KW} ${test[0]}
Set Test Variable ${INDEX} ${1}
Check Variable Message \${/} = * pattern=yes
Check Variable Message \${:} = ${:}
@@ -67,7 +67,7 @@ Log Variables In Test
Check Variable Message \${LOG_LEVEL} = TRACE
Check Variable Message \${None} = None
Check Variable Message \${null} = None
- Check Variable Message \&{OPTIONS} = { include=[] | exclude=[] | skip=[] | skip_on_failure=[] }
+ Check Variable Message \&{OPTIONS} = { rpa=False | include=[] | exclude=[] | skip=[] | skip_on_failure=[] | console_width=78 }
Check Variable Message \${OUTPUT_DIR} = * pattern=yes
Check Variable Message \${OUTPUT_FILE} = * pattern=yes
Check Variable Message \${PREV_TEST_MESSAGE} =
@@ -93,7 +93,7 @@ Log Variables In Test
Log Variables After Setting New Variables
${test} = Check Test Case Log Variables
- Set Test Variable ${KW} ${test.body[4]}
+ Set Test Variable ${KW} ${test[4]}
Set Test Variable ${INDEX} ${1}
Check Variable Message \${/} = * DEBUG pattern=yes
Check Variable Message \${:} = ${:} DEBUG
@@ -114,7 +114,7 @@ Log Variables After Setting New Variables
Check Variable Message \${LOG_LEVEL} = TRACE DEBUG
Check Variable Message \${None} = None DEBUG
Check Variable Message \${null} = None DEBUG
- Check Variable Message \&{OPTIONS} = { include=[] | exclude=[] | skip=[] | skip_on_failure=[] } DEBUG
+ Check Variable Message \&{OPTIONS} = { rpa=False | include=[] | exclude=[] | skip=[] | skip_on_failure=[] | console_width=78 } DEBUG
Check Variable Message \${OUTPUT_DIR} = * DEBUG pattern=yes
Check Variable Message \${OUTPUT_FILE} = * DEBUG pattern=yes
Check Variable Message \${PREV_TEST_MESSAGE} = DEBUG
@@ -141,7 +141,7 @@ Log Variables After Setting New Variables
Log Variables In User Keyword
${test} = Check Test Case Log Variables
- Set Test Variable ${KW} ${test.body[5].body[2]}
+ Set Test Variable ${KW} ${test[5, 2]}
Set Test Variable ${INDEX} ${1}
Check Variable Message \${/} = * pattern=yes
Check Variable Message \${:} = ${:}
@@ -160,7 +160,7 @@ Log Variables In User Keyword
Check Variable Message \${LOG_LEVEL} = TRACE
Check Variable Message \${None} = None
Check Variable Message \${null} = None
- Check Variable Message \&{OPTIONS} = { include=[] | exclude=[] | skip=[] | skip_on_failure=[] }
+ Check Variable Message \&{OPTIONS} = { rpa=False | include=[] | exclude=[] | skip=[] | skip_on_failure=[] | console_width=78 }
Check Variable Message \${OUTPUT_DIR} = * pattern=yes
Check Variable Message \${OUTPUT_FILE} = * pattern=yes
Check Variable Message \${PREV_TEST_MESSAGE} =
@@ -191,5 +191,5 @@ List and dict variables failing during iteration
*** Keywords ***
Check Variable Message
[Arguments] ${expected} ${level}=INFO ${pattern}=
- Check Log Message ${KW.msgs[${INDEX}]} ${expected} ${level} pattern=${pattern}
+ Check Log Message ${KW[${INDEX}]} ${expected} ${level} pattern=${pattern}
Set Test Variable ${INDEX} ${INDEX + 1}
diff --git a/atest/robot/standard_libraries/builtin/misc.robot b/atest/robot/standard_libraries/builtin/misc.robot
index 38994c9afac..a7af9cba20c 100644
--- a/atest/robot/standard_libraries/builtin/misc.robot
+++ b/atest/robot/standard_libraries/builtin/misc.robot
@@ -11,9 +11,9 @@ Catenate
Comment
${tc} = Check Test Case ${TEST NAME}
- Should Be Empty ${tc.kws[0].msgs}
- Should Be Empty ${tc.kws[1].msgs}
- Should Be Empty ${tc.kws[2].msgs}
+ Should Be Empty ${tc[0].body}
+ Should Be Empty ${tc[1].body}
+ Should Be Empty ${tc[2].body}
Regexp Escape
Check Test Case ${TEST NAME}
diff --git a/atest/robot/standard_libraries/builtin/reload_library.robot b/atest/robot/standard_libraries/builtin/reload_library.robot
index 25cb84aad6a..97bf6e4d58e 100644
--- a/atest/robot/standard_libraries/builtin/reload_library.robot
+++ b/atest/robot/standard_libraries/builtin/reload_library.robot
@@ -5,12 +5,12 @@ Resource atest_resource.robot
*** Test Cases ***
Reload and add keyword
${tc}= Check Test Case ${TESTNAME}
- Check log message ${tc.kws[2].msgs[0]} Reloaded library Reloadable with 7 keywords.
+ Check log message ${tc[2, 0]} Reloaded library Reloadable with 7 keywords.
Reloading changes args
${tc}= Check Test Case ${TESTNAME}
- Should be equal ${tc.kws[0].doc} Doc for original 1 with args arg
- Should be equal ${tc.kws[3].doc} Doc for original 1 with args arg1, arg2
+ Should be equal ${tc[0].doc} Doc for original 1 with args arg
+ Should be equal ${tc[3].doc} Doc for original 1 with args arg1, arg2
Reloading can remove a keyword
Check Test Case ${TESTNAME}
@@ -32,8 +32,8 @@ Reloading None fails
Static library
${tc}= Check Test Case ${TESTNAME}
- Should be equal ${tc.kws[2].doc} This doc for static
+ Should be equal ${tc[2].doc} This doc for static
Module library
${tc}= Check Test Case ${TESTNAME}
- Should be equal ${tc.kws[3].doc} This doc for module
+ Should be equal ${tc[3].doc} This doc for module
diff --git a/atest/robot/standard_libraries/builtin/reload_with_name.robot b/atest/robot/standard_libraries/builtin/reload_with_name.robot
index 7b04683ff74..dcabc6ea7f6 100644
--- a/atest/robot/standard_libraries/builtin/reload_with_name.robot
+++ b/atest/robot/standard_libraries/builtin/reload_with_name.robot
@@ -5,7 +5,7 @@ Resource atest_resource.robot
*** Test Cases ***
Reload with name
${tc}= Check Test Case ${TESTNAME}
- Check log message ${tc.kws[1].msgs[0]} Reloaded library foo with 7 keywords.
+ Check log message ${tc[1, 0]} Reloaded library foo with 7 keywords.
Reload with instance
Check Test Case ${TESTNAME}
diff --git a/atest/robot/standard_libraries/builtin/repeat_keyword.robot b/atest/robot/standard_libraries/builtin/repeat_keyword.robot
index 5c0e3ea1e9e..b4cfbae94c2 100644
--- a/atest/robot/standard_libraries/builtin/repeat_keyword.robot
+++ b/atest/robot/standard_libraries/builtin/repeat_keyword.robot
@@ -5,27 +5,27 @@ Resource atest_resource.robot
*** Test Cases ***
Times As String
${tc} = Check Test Case ${TEST NAME}
- Check Repeated Messages ${tc.kws[0]} 2 Hello, repeating world!
+ Check Repeated Messages ${tc[0]} 2 Hello, repeating world!
Times As Integer
${tc} = Check Test Case ${TEST NAME}
- Check Repeated Messages ${tc.kws[0]} 42 This works too!!
+ Check Repeated Messages ${tc[0]} 42 This works too!!
Times With 'times' Postfix
${tc} = Check Test Case ${TEST NAME}
- Check Repeated Messages ${tc.kws[0]} 3 This is done 3 times
- Check Repeated Messages ${tc.kws[1]} 2 Case and space insensitive
+ Check Repeated Messages ${tc[0]} 3 This is done 3 times
+ Check Repeated Messages ${tc[1]} 2 Case and space insensitive
Times With 'x' Postfix
${tc} = Check Test Case ${TEST NAME}
- Check Repeated Messages ${tc.kws[0]} 10 Close to old repeating syntax
- Check Repeated Messages ${tc.kws[1]} 1 Case and space
+ Check Repeated Messages ${tc[0]} 10 Close to old repeating syntax
+ Check Repeated Messages ${tc[1]} 1 Case and space
Zero And Negative Times
${tc} = Check Test Case ${TEST NAME}
- Check Repeated Messages ${tc.kws[0]} 0 name=This is not executed
- Check Repeated Messages ${tc.kws[2]} 0 name=\${name}
- Check Repeated Messages ${tc.kws[3]} 0 name=This is not executed
+ Check Repeated Messages ${tc[0]} 0 name=This is not executed
+ Check Repeated Messages ${tc[2]} 0 name=\${name}
+ Check Repeated Messages ${tc[3]} 0 name=This is not executed
Invalid Times
Check Test Case Invalid Times 1
@@ -33,16 +33,16 @@ Invalid Times
Repeat Keyword With Time String
${tc} = Check Test Case ${TEST NAME}
- Check Repeated Messages With Time ${tc.kws[0]} This is done for 00:00:00.003
- Check Repeated Messages With Time ${tc.kws[1]} This is done for 3 milliseconds
- Check Repeated Messages With Time ${tc.kws[2]} This is done for 3ms
+ Check Repeated Messages With Time ${tc[0]} This is done for 00:00:00.003
+ Check Repeated Messages With Time ${tc[1]} This is done for 3 milliseconds
+ Check Repeated Messages With Time ${tc[2]} This is done for 3ms
Repeat Keyword Arguments As Variables
${tc} = Check Test Case ${TEST_NAME}
- Check Repeated Keyword Name ${tc.kws[1]} 2 BuiltIn.Should Be Equal
- Check Repeated Keyword Name ${tc.kws[3]} 42 BuiltIn.Should Be Equal
- Check Repeated Keyword Name ${tc.kws[5]} 10 BuiltIn.No Operation
- Check Repeated Keyword Name ${tc.kws[7]} 1 BuiltIn.Should Be Equal
+ Check Repeated Keyword Name ${tc[1]} 2 BuiltIn.Should Be Equal
+ Check Repeated Keyword Name ${tc[3]} 42 BuiltIn.Should Be Equal
+ Check Repeated Keyword Name ${tc[5]} 10 BuiltIn.No Operation
+ Check Repeated Keyword Name ${tc[7]} 1 BuiltIn.Should Be Equal
Repeated Keyword As Non-existing Variable
Check Test Case ${TEST_NAME}
@@ -56,47 +56,52 @@ Repeated Keyword Failing
Repeat Keyword With Continuable Failure
${tc} = Check Test Case ${TEST_NAME}
- Length Should Be ${tc.kws[0].kws} 3
+ Length Should Be ${tc[0].body} 6
+ Length Should Be ${tc[0].messages} 3
Repeat Keyword With Failure After Continuable Failure
${tc} = Check Test Case ${TEST_NAME}
- Length Should Be ${tc.kws[0].kws} 2
+ Length Should Be ${tc[0].body} 4
+ Length Should Be ${tc[0].messages} 2
Repeat Keyword With Pass Execution
${tc} = Check Test Case ${TEST_NAME}
- Length Should Be ${tc.kws[0].kws} 1
+ Length Should Be ${tc[0].body} 2
+ Length Should Be ${tc[0].messages} 1
Repeat Keyword With Pass Execution After Continuable Failure
${tc} = Check Test Case ${TEST_NAME}
- Length Should Be ${tc.kws[0].kws} 2
+ Length Should Be ${tc[0].body} 4
+ Length Should Be ${tc[0].messages} 2
*** Keywords ***
Check Repeated Messages
- [Arguments] ${kw} ${count} ${msg}= ${name}=
- Length Should Be ${kw.kws} ${count}
- FOR ${i} IN RANGE ${count}
- Check Log Message ${kw.msgs[${i}]} Repeating keyword, round ${i+1}/${count}.
- Check Log Message ${kw.kws[${i}].msgs[0]} ${msg}
- END
- IF ${count} != 0
- Length Should Be ${kw.msgs} ${count}
+ [Arguments] ${kw} ${rounds} ${msg}= ${name}=
+ IF ${rounds} == 0
+ Length Should Be ${kw.body} 1
+ Check Log Message ${kw[0]} Keyword '${name}' repeated zero times.
ELSE
- Check Log Message ${kw.msgs[0]} Keyword '${name}' repeated zero times.
+ Length Should Be ${kw.body} ${{int($rounds) * 2}}
+ Length Should Be ${kw.messages} ${rounds}
+ END
+ FOR ${i} IN RANGE ${rounds}
+ Check Log Message ${kw[${i * 2}]} Repeating keyword, round ${i+1}/${rounds}.
+ Check Log Message ${kw[${i * 2 + 1}, 0]} ${msg}
END
Check Repeated Messages With Time
[Arguments] ${kw} ${msg}=${None}
- Should Not Be Empty ${kw.kws}
- FOR ${i} IN RANGE ${{len($kw.kws)}}
- Check Log Message ${kw.msgs[${i}]}
+ Should Be True len($kw.body) / 2 == len($kw.messages) and len($kw.body) > 0
+ FOR ${i} IN RANGE ${{len($kw.messages)}}
+ Check Log Message ${kw[${i * 2}]}
... Repeating keyword, round ${i+1}, *remaining. pattern=yes
- Check Log Message ${kw.kws[${i}].msgs[0]} ${msg}
+ Check Log Message ${kw[${i * 2 + 1}, 0]} ${msg}
END
- Should Be Equal ${{len($kw.msgs)}} ${{len($kw.kws)}}
+ Should Be Equal ${{len($kw.messages) * 2}} ${{len($kw.body)}}
Check Repeated Keyword Name
[Arguments] ${kw} ${count} ${name}=${None}
- Length Should Be ${kw.kws} ${count}
- FOR ${i} IN RANGE ${count}
- Should Be Equal ${kw.kws[${i}].full_name} ${name}
+ Should Be True len($kw.body) / 2 == len($kw.messages) == ${count}
+ FOR ${i} IN RANGE 1 ${count} * 2 2
+ Should Be Equal ${kw[${i}].full_name} ${name}
END
diff --git a/atest/robot/standard_libraries/builtin/run_keyword.robot b/atest/robot/standard_libraries/builtin/run_keyword.robot
index a887910bc04..b002afd32b2 100644
--- a/atest/robot/standard_libraries/builtin/run_keyword.robot
+++ b/atest/robot/standard_libraries/builtin/run_keyword.robot
@@ -4,80 +4,92 @@ Resource atest_resource.robot
*** Test Cases ***
Run Keyword
- ${tc} = Check test Case ${TEST NAME}
- Check Run Keyword ${tc.kws[0]} BuiltIn.Log This is logged with Run Keyword
- Check Keyword Data ${tc.kws[1].kws[0]} BuiltIn.No Operation
- Check Run Keyword ${tc.kws[2]} BuiltIn.Log Many 1 2 3 4 5
- Check Run Keyword ${tc.kws[4]} BuiltIn.Log Run keyword with variable: Log
- Check Run Keyword ${tc.kws[6]} BuiltIn.Log Many one two
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Run Keyword ${tc[0]} BuiltIn.Log This is logged with Run Keyword
+ Check Keyword Data ${tc[1, 0]} BuiltIn.No Operation
+ Check Run Keyword ${tc[2]} BuiltIn.Log Many 1 2 3 4 5
+ Check Run Keyword ${tc[4]} BuiltIn.Log Run keyword with variable: Log
+ Check Run Keyword ${tc[6]} BuiltIn.Log Many one two
Run Keyword Returning Value
- ${tc} = Check test Case ${TEST NAME}
- Check Keyword Data ${tc.kws[0]} BuiltIn.Run Keyword \${ret} Set Variable, hello world
- Check Keyword Data ${tc.kws[0].kws[0]} BuiltIn.Set Variable args=hello world
- Check Keyword Data ${tc.kws[2]} BuiltIn.Run Keyword \${ret} Evaluate, 1+2
- Check Keyword Data ${tc.kws[2].kws[0]} BuiltIn.Evaluate args=1+2
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Keyword Data ${tc[0]} BuiltIn.Run Keyword \${ret} Set Variable, hello world
+ Check Keyword Data ${tc[0, 0]} BuiltIn.Set Variable args=hello world
+ Check Keyword Data ${tc[2]} BuiltIn.Run Keyword \${ret} Evaluate, 1+2
+ Check Keyword Data ${tc[2, 0]} BuiltIn.Evaluate args=1+2
Run Keyword With Arguments That Needs To Be Escaped
- ${tc} = Check test Case ${TEST NAME}
- Check Log Message ${tc.kws[1].kws[0].msgs[0]} c:\\temp\\foo
- Check Log Message ${tc.kws[1].kws[0].msgs[1]} \${notvar}
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Log Message ${tc[1, 0, 0]} c:\\temp\\foo
+ Check Log Message ${tc[1, 0, 1]} \${notvar}
Escaping Arguments From Opened List Variable
- ${tc} = Check test Case ${TEST NAME}
- Check Log Message ${tc.kws[1].kws[0].msgs[0]} message=foo
- Check Log Message ${tc.kws[3].kws[0].msgs[0]} 42
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Log Message ${tc[1, 0, 0]} message=foo
+ Check Log Message ${tc[3, 0, 0]} 42
Run Keyword With UK
- ${tc} = Check test Case ${TEST NAME}
- Check Run Keyword In UK ${tc.kws[0]} BuiltIn.Log Using UK
- Check Run Keyword In UK ${tc.kws[1]} BuiltIn.Log Many yksi kaksi
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Run Keyword In UK ${tc[0]} BuiltIn.Log Using UK
+ Check Run Keyword In UK ${tc[1]} BuiltIn.Log Many yksi kaksi
Run Keyword In Multiple Levels And With UK
- Check test Case ${TEST NAME}
+ Check Test Case ${TEST NAME}
With keyword accepting embedded arguments
- ${tc} = Check test Case ${TEST NAME}
- Check Run Keyword With Embedded Args ${tc.kws[0]} Embedded "arg" arg
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Run Keyword With Embedded Args ${tc[0]} Embedded "arg" arg
With library keyword accepting embedded arguments
- ${tc} = Check test Case ${TEST NAME}
- Check Run Keyword With Embedded Args ${tc.kws[0]} Embedded "arg" in library arg
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Run Keyword With Embedded Args ${tc[0]} Embedded "arg" in library arg
With keyword accepting embedded arguments as variables
- ${tc} = Check test Case ${TEST NAME}
- Check Run Keyword With Embedded Args ${tc.kws[0]} Embedded "\${VARIABLE}" value
- Check Run Keyword With Embedded Args ${tc.kws[1]} Embedded "\${1}" 1
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Run Keyword With Embedded Args ${tc[0]} Embedded "\${VARIABLE}" value
+ Check Run Keyword With Embedded Args ${tc[1]} Embedded "\${1}" 1
With library keyword accepting embedded arguments as variables
- ${tc} = Check test Case ${TEST NAME}
- Check Run Keyword With Embedded Args ${tc.kws[0]} Embedded "\${VARIABLE}" in library value
- Check Run Keyword With Embedded Args ${tc.kws[1]} Embedded "\${1}" in library 1
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Run Keyword With Embedded Args ${tc[0]} Embedded "\${VARIABLE}" in library value
+ Check Run Keyword With Embedded Args ${tc[1]} Embedded "\${1}" in library 1
With keyword accepting embedded arguments as variables containing objects
- ${tc} = Check test Case ${TEST NAME}
- Check Run Keyword With Embedded Args ${tc.kws[0]} Embedded "\${OBJECT}" Robot
- Check Run Keyword With Embedded Args ${tc.kws[1]} Embedded object "\${OBJECT}" Robot
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Run Keyword With Embedded Args ${tc[0]} Embedded "\${OBJECT}" Robot
+ Check Run Keyword With Embedded Args ${tc[1]} Embedded object "\${OBJECT}" Robot
With library keyword accepting embedded arguments as variables containing objects
- ${tc} = Check test Case ${TEST NAME}
- Check Run Keyword With Embedded Args ${tc.kws[0]} Embedded "\${OBJECT}" in library Robot
- Check Run Keyword With Embedded Args ${tc.kws[1]} Embedded object "\${OBJECT}" in library Robot
-
-Run Keyword In For Loop
- ${tc} = Check test Case ${TEST NAME}
- Check Run Keyword ${tc.kws[0].kws[0].kws[0]} BuiltIn.Log hello from for loop
- Check Run Keyword In UK ${tc.kws[0].kws[2].kws[0]} BuiltIn.Log hei maailma
- Check Run Keyword ${tc.kws[1].kws[0].kws[0]} BuiltIn.Log hello from second for loop
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Run Keyword With Embedded Args ${tc[0]} Embedded "\${OBJECT}" in library Robot
+ Check Run Keyword With Embedded Args ${tc[1]} Embedded object "\${OBJECT}" in library Robot
+
+Embedded arguments matching only after replacing variables
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Run Keyword With Embedded Args ${tc[1]} Embedded "arg" arg
+ Check Run Keyword With Embedded Args ${tc[2]} Embedded "arg" in library arg
+
+Exact match after replacing variables has higher precedence than embedded arguments
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Run Keyword ${tc[1]} Embedded "not"
+ Check Log Message ${tc[1][0][0][0]} Nothing embedded in this user keyword!
+ Check Run Keyword ${tc[2]} embedded_args.Embedded "not" in library
+ Check Log Message ${tc[2][0][0]} Nothing embedded in this library keyword!
+
+Run Keyword In FOR Loop
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Run Keyword ${tc[0, 0, 0]} BuiltIn.Log hello from for loop
+ Check Run Keyword In UK ${tc[0, 2, 0]} BuiltIn.Log hei maailma
+ Check Run Keyword ${tc[1, 0, 0]} BuiltIn.Log hello from second for loop
Run Keyword With Test Timeout
Check Test Case ${TEST NAME} Passing
${tc} = Check Test Case ${TEST NAME} Exceeded
- Check Run Keyword ${tc.kws[0]} BuiltIn.Log Before Timeout
+ Check Run Keyword ${tc[0]} BuiltIn.Log Before Timeout
Run Keyword With KW Timeout
- Check test Case ${TEST NAME} Passing
- Check test Case ${TEST NAME} Exceeded
+ Check Test Case ${TEST NAME} Passing
+ Check Test Case ${TEST NAME} Exceeded
Run Keyword With Invalid Keyword Name
Check Test Case ${TEST NAME}
@@ -97,27 +109,27 @@ Stdout and stderr are not captured when running Run Keyword
*** Keywords ***
Check Run Keyword
- [Arguments] ${kw} ${subkw_name} @{msgs}
- Should Be Equal ${kw.full_name} BuiltIn.Run Keyword
- Should Be Equal ${kw.kws[0].full_name} ${subkw_name}
+ [Arguments] ${kw} ${name} @{msgs}
+ Should Be Equal ${kw.full_name} BuiltIn.Run Keyword
+ Should Be Equal ${kw[0].full_name} ${name}
FOR ${index} ${msg} IN ENUMERATE @{msgs}
- Check Log Message ${kw.kws[0].msgs[${index}]} ${msg}
+ Check Log Message ${kw[0, ${index}]} ${msg}
END
Check Run Keyword In Uk
[Arguments] ${kw} ${subkw_name} @{msgs}
- Should Be Equal ${kw.full_name} BuiltIn.Run Keyword
- Should Be Equal ${kw.kws[0].full_name} My UK
- Check Run Keyword ${kw.kws[0].kws[0]} ${subkw_name} @{msgs}
+ Should Be Equal ${kw.full_name} BuiltIn.Run Keyword
+ Should Be Equal ${kw[0].full_name} My UK
+ Check Run Keyword ${kw[0, 0]} ${subkw_name} @{msgs}
Check Run Keyword With Embedded Args
[Arguments] ${kw} ${subkw_name} ${msg}
Should Be Equal ${kw.full_name} BuiltIn.Run Keyword
IF ${subkw_name.endswith('library')}
- Should Be Equal ${kw.kws[0].full_name} embedded_args.${subkw_name}
- Check Log Message ${kw.kws[0].msgs[0]} ${msg}
+ Should Be Equal ${kw[0].full_name} embedded_args.${subkw_name}
+ Check Log Message ${kw[0, 0]} ${msg}
ELSE
- Should Be Equal ${kw.kws[0].full_name} ${subkw_name}
- Should Be Equal ${kw.kws[0].kws[0].full_name} BuiltIn.Log
- Check Log Message ${kw.kws[0].kws[0].msgs[0]} ${msg}
+ Should Be Equal ${kw[0].full_name} ${subkw_name}
+ Should Be Equal ${kw[0, 0].full_name} BuiltIn.Log
+ Check Log Message ${kw[0, 0, 0]} ${msg}
END
diff --git a/atest/robot/standard_libraries/builtin/run_keyword_and_continue_on_failure.robot b/atest/robot/standard_libraries/builtin/run_keyword_and_continue_on_failure.robot
index 3f83802c905..2af70af225d 100644
--- a/atest/robot/standard_libraries/builtin/run_keyword_and_continue_on_failure.robot
+++ b/atest/robot/standard_libraries/builtin/run_keyword_and_continue_on_failure.robot
@@ -5,16 +5,16 @@ Resource atest_resource.robot
*** Test Cases ***
Run Keyword And Continue On Failure
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} Expected Failure FAIL
- Check Log Message ${tc.kws[1].kws[0].msgs[0]} Expected Failure 2 FAIL
- Check Log Message ${tc.kws[2].msgs[0]} This should be executed
+ Check Log Message ${tc[0, 0, 0]} Expected Failure FAIL
+ Check Log Message ${tc[1, 0, 0]} Expected Failure 2 FAIL
+ Check Log Message ${tc[2, 0]} This should be executed
Run Keyword And Continue On Failure In For Loop
Check Test Case ${TESTNAME}
Run User keyword And Continue On Failure
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[1].msgs[0]} This should be executed
+ Check Log Message ${tc[1, 0]} This should be executed
Run Keyword And Continue On Failure With For Loops
Check Test Case ${TESTNAME}
diff --git a/atest/robot/standard_libraries/builtin/run_keyword_and_return.robot b/atest/robot/standard_libraries/builtin/run_keyword_and_return.robot
index b0fbeccc939..9e345446a7f 100644
--- a/atest/robot/standard_libraries/builtin/run_keyword_and_return.robot
+++ b/atest/robot/standard_libraries/builtin/run_keyword_and_return.robot
@@ -5,15 +5,15 @@ Resource atest_resource.robot
*** Test Cases ***
Return one value
${tc} = Check Test Case ${TESTNAME}
- Check log message ${tc.kws[0].kws[0].msgs[0]} Returning from the enclosing user keyword.
+ Check Log Message ${tc[0, 0, 1]} Returning from the enclosing user keyword.
Return multiple values
Check Test Case ${TESTNAME}
Return nothing
${tc} = Check Test Case ${TESTNAME}
- Check log message ${tc.kws[0].kws[0].kws[0].msgs[0]} No return value
- Check log message ${tc.kws[0].kws[0].msgs[0]} Returning from the enclosing user keyword.
+ Check Log Message ${tc[0, 0, 0, 0]} No return value
+ Check Log Message ${tc[0, 0, 1]} Returning from the enclosing user keyword.
Nested usage
Check Test Case ${TESTNAME}
@@ -23,13 +23,13 @@ Keyword fails
Inside Run Keyword variants
${tc} = Check Test Case ${TESTNAME}
- Check log message ${tc.kws[2].kws[0].kws[0].msgs[0]} First keyword
- Check log message ${tc.kws[2].kws[0].kws[2].msgs[0]} Returning from the enclosing user keyword.
+ Check Log Message ${tc[2, 0, 0, 0]} First keyword
+ Check Log Message ${tc[2, 0, 2, 1]} Returning from the enclosing user keyword.
Using Run Keyword variants
${tc} = Check Test Case ${TESTNAME}
- Check log message ${tc.kws[2].kws[0].kws[0].kws[1].msgs[0]} Second keyword
- Check log message ${tc.kws[2].kws[0].kws[0].kws[2].msgs[0]} Returning from the enclosing user keyword.
+ Check Log Message ${tc[2, 0, 0, 1, 0]} Second keyword
+ Check Log Message ${tc[2, 0, 0, 2, 1]} Returning from the enclosing user keyword.
Outside user keyword
Check Test Case ${TESTNAME}
@@ -42,9 +42,9 @@ Return strings that needs to be escaped
Run Keyword And Return If
${tc} = Check Test Case ${TESTNAME}
- Check log message ${tc.kws[0].kws[1].msgs[0]} Returning from the enclosing user keyword.
- Check log message ${tc.kws[2].kws[1].msgs[0]} Returning from the enclosing user keyword.
- Check log message ${tc.kws[4].kws[0].kws[2].kws[0].msgs[0]} Returning from the enclosing user keyword.
+ Check Log Message ${tc[0, 1, 1]} Returning from the enclosing user keyword.
+ Check Log Message ${tc[2, 1, 1]} Returning from the enclosing user keyword.
+ Check Log Message ${tc[4, 0, 2, 0, 1]} Returning from the enclosing user keyword.
Run Keyword And Return If can have non-existing keywords and variables if condition is not true
Check Test Case ${TESTNAME}
diff --git a/atest/robot/standard_libraries/builtin/run_keyword_and_warn_on_failure.robot b/atest/robot/standard_libraries/builtin/run_keyword_and_warn_on_failure.robot
index 721feb89810..442c10ac1c5 100644
--- a/atest/robot/standard_libraries/builtin/run_keyword_and_warn_on_failure.robot
+++ b/atest/robot/standard_libraries/builtin/run_keyword_and_warn_on_failure.robot
@@ -5,16 +5,16 @@ Resource atest_resource.robot
*** Test Cases ***
Run Keyword And Warn On Failure
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} Executing keyword 'FAIL' failed:\nExpected Warn WARN
+ Check Log Message ${tc[0, 1]} Executing keyword 'FAIL' failed:\nExpected Warn WARN
Run Keyword And Warn On Failure For Keyword Teardown
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]}
+ Check Log Message ${tc[0, 1]}
... Executing keyword 'Failing Keyword Teardown' failed:\nKeyword teardown failed:\nExpected WARN
Run User keyword And Warn On Failure
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]}
+ Check Log Message ${tc[0, 1]}
... Executing keyword 'Exception In User Defined Keyword' failed:\nExpected Warn In User Keyword WARN
Run Keyword And Warn On Failure With Syntax Error
@@ -22,7 +22,7 @@ Run Keyword And Warn On Failure With Syntax Error
Run Keyword And Warn On Failure With Failure On Test Teardown
${tc}= Check Test Case ${TESTNAME}
- Check Log Message ${tc.teardown.msgs[0]}
+ Check Log Message ${tc.teardown[1]}
... Executing keyword 'Should Be Equal' failed:\nx != y WARN
Run Keyword And Warn On Failure With Timeout
@@ -30,5 +30,5 @@ Run Keyword And Warn On Failure With Timeout
Run Keyword And Warn On Failure On Suite Teardown
${suite} = Get Test Suite Run Keyword And Warn On Failure
- Check Log Message ${suite.teardown.msgs[0]}
+ Check Log Message ${suite.teardown[1]}
... Executing keyword 'Fail' failed:\nExpected Warn From Suite Teardown WARN
diff --git a/atest/robot/standard_libraries/builtin/run_keyword_based_on_suite_stats.robot b/atest/robot/standard_libraries/builtin/run_keyword_based_on_suite_stats.robot
index 71c1c1fea88..0ac6326e777 100644
--- a/atest/robot/standard_libraries/builtin/run_keyword_based_on_suite_stats.robot
+++ b/atest/robot/standard_libraries/builtin/run_keyword_based_on_suite_stats.robot
@@ -6,8 +6,8 @@ Resource atest_resource.robot
Run Keyword If All Tests Passed
${suite} = Get Test Suite Run Keyword If All Tests Passed When All Pass
Should Be Equal As Integers ${suite.statistics.failed} 0
- Should Be Equal ${suite.teardown.kws[0].name} My Teardown
- Check Log Message ${suite.teardown.kws[0].kws[0].msgs[0]} Suite teardown message
+ Should Be Equal ${suite.teardown[0].name} My Teardown
+ Check Log Message ${suite.teardown[0, 0, 0]} Suite teardown message
Run Keyword If All Tests Passed Can't be Used In Test
Check Test Case Run Keyword If All Tests Passed Can't be Used In Test
@@ -18,8 +18,8 @@ Run Keyword If All tests Passed Is not Executed When Any Test Fails
Run Keyword If Any Tests Failed
${suite} = Get Test Suite Run Keyword If Any Tests Failed When Test Fails
Should Be Equal As Integers ${suite.statistics.failed} 1
- Should Be Equal ${suite.teardown.kws[0].name} My Teardown
- Check Log Message ${suite.teardown.kws[0].kws[0].msgs[0]} Suite teardown message
+ Should Be Equal ${suite.teardown[0].name} My Teardown
+ Check Log Message ${suite.teardown[0, 0, 0]} Suite teardown message
Run Keyword If Any Tests Failed Can't be Used In Test
Check Test Case Run Keyword If Any Tests Failed Can't be Used In Test
diff --git a/atest/robot/standard_libraries/builtin/run_keyword_if_test_passed_failed.robot b/atest/robot/standard_libraries/builtin/run_keyword_if_test_passed_failed.robot
index 5d8a75270be..b635c2444c6 100644
--- a/atest/robot/standard_libraries/builtin/run_keyword_if_test_passed_failed.robot
+++ b/atest/robot/standard_libraries/builtin/run_keyword_if_test_passed_failed.robot
@@ -5,12 +5,12 @@ Resource atest_resource.robot
*** Test Cases ***
Run Keyword If Test Failed when test fails
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.teardown.body[0].full_name} BuiltIn.Log
- Check Log Message ${tc.teardown.body[0].msgs[0]} Hello from teardown!
+ Should Be Equal ${tc.teardown[0].full_name} BuiltIn.Log
+ Check Log Message ${tc.teardown[0, 0]} Hello from teardown!
Run Keyword If Test Failed in user keyword when test fails
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.teardown.body[1].body[0].msgs[0]} Apparently test failed! FAIL
+ Check Log Message ${tc.teardown[1, 0, 0]} Apparently test failed! FAIL
Run Keyword If Test Failed when test passes
${tc} = Check Test Case ${TEST NAME}
@@ -18,7 +18,7 @@ Run Keyword If Test Failed when test passes
Run Keyword If Test Failed in user keyword when test passes
${tc} = Check Test Case ${TEST NAME}
- Should Be Empty ${tc.teardown.body[1].body}
+ Should Be Empty ${tc.teardown[1].body}
Run Keyword If Test Failed when test is skipped
${tc} = Check Test Case ${TEST NAME}
@@ -26,35 +26,35 @@ Run Keyword If Test Failed when test is skipped
Run Keyword If Test Failed in user keyword when test is skipped
${tc} = Check Test Case ${TEST NAME}
- Should Be Empty ${tc.teardown.body[1].body}
+ Should Be Empty ${tc.teardown[1].body}
Run Keyword If Test Failed Can't Be Used In Setup
${tc} = Check Test Case ${TEST NAME}
Length Should Be ${tc.setup.body} 1
- Check Log Message ${tc.setup.body[0]} Keyword 'Run Keyword If Test Failed' can only be used in test teardown. FAIL
+ Check Log Message ${tc.setup[0]} Keyword 'Run Keyword If Test Failed' can only be used in test teardown. FAIL
Run Keyword If Test Failed Can't Be Used in Test
Check Test Case ${TEST NAME}
Run Keyword If Test Failed Uses User Keyword
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.teardown.kws[0].kws[0].msgs[0]} Teardown message
+ Check Log Message ${tc.teardown[0, 0, 0]} Teardown message
Run Keyword If Test Failed Fails
Check Test Case ${TEST NAME}
Run Keyword If test Failed Can't Be Used In Suite Setup or Teardown
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${SUITE.suites[0].setup.msgs[0]} Keyword 'Run Keyword If Test Failed' can only be used in test teardown. FAIL
- Check Log Message ${SUITE.suites[0].teardown.msgs[0]} Keyword 'Run Keyword If Test Failed' can only be used in test teardown. FAIL
+ Check Log Message ${SUITE.suites[0].setup[0]} Keyword 'Run Keyword If Test Failed' can only be used in test teardown. FAIL
+ Check Log Message ${SUITE.suites[0].teardown[0]} Keyword 'Run Keyword If Test Failed' can only be used in test teardown. FAIL
Run Keyword If Test Passed when test passes
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.teardown.body[0].msgs[0]} Teardown of passing test
+ Check Log Message ${tc.teardown[0, 0]} Teardown of passing test
Run Keyword If Test Passed in user keyword when test passes
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.teardown.body[1].body[0].msgs[0]} Apparently test passed! FAIL
+ Check Log Message ${tc.teardown[1, 0, 0]} Apparently test passed! FAIL
Run Keyword If Test Passed when test fails
${tc} = Check Test Case ${TEST NAME}
@@ -62,7 +62,7 @@ Run Keyword If Test Passed when test fails
Run Keyword If Test Passed in user keyword when test fails
${tc} = Check Test Case ${TEST NAME}
- Should Be Empty ${tc.teardown.body[1].body}
+ Should Be Empty ${tc.teardown[1].body}
Run Keyword If Test Passed when test is skipped
${tc} = Check Test Case ${TEST NAME}
@@ -70,7 +70,7 @@ Run Keyword If Test Passed when test is skipped
Run Keyword If Test Passed in user keyword when test is skipped
${tc} = Check Test Case ${TEST NAME}
- Should Be Empty ${tc.teardown.body[1].body}
+ Should Be Empty ${tc.teardown[1].body}
Run Keyword If Test Passed Can't Be used In Setup
Check Test Case ${TEST NAME}
@@ -80,8 +80,8 @@ Run Keyword If Test Passed Can't Be used In Test
Run Keyword If Test Passes Uses User Keyword
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.teardown.kws[0].kws[0].msgs[0]} Teardown message
- Check Keyword Data ${tc.teardown.kws[0].kws[0]} BuiltIn.Log args=\${message}
+ Check Log Message ${tc.teardown[0, 0, 0]} Teardown message
+ Check Keyword Data ${tc.teardown[0, 0]} BuiltIn.Log args=\${message}
Run Keyword If Test Passed Fails
Check Test Case ${TEST NAME}
@@ -94,27 +94,27 @@ Run Keyword If Test Failed When Teardown Fails
Run Keyword If Test Passed/Failed With Earlier Ignored Failures
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.teardown.kws[0].kws[0].status} FAIL
- Should Be Equal ${tc.teardown.kws[0].status} PASS
- Should Be Equal ${tc.teardown.kws[1].kws[0].status} FAIL
- Should Be Equal ${tc.teardown.kws[1].status} PASS
- Should Be Equal ${tc.teardown.status} PASS
+ Should Be Equal ${tc.teardown[0, 0].status} FAIL
+ Should Be Equal ${tc.teardown[0].status} PASS
+ Should Be Equal ${tc.teardown[1, 0].status} FAIL
+ Should Be Equal ${tc.teardown[1].status} PASS
+ Should Be Equal ${tc.teardown.status} PASS
Run Keyword If Test Passed/Failed after skip in teardown
${tc} = Check Test Case ${TEST NAME}
- Should Be Empty ${tc.teardown.body[1].body}
- Should Be Empty ${tc.teardown.body[2].body}
+ Should Be Empty ${tc.teardown[1].body}
+ Should Be Empty ${tc.teardown[2].body}
Continuable Failure In Teardown
Check Test Case ${TEST NAME}
Run Keyword If test Passed Can't Be Used In Suite Setup or Teardown
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${SUITE.suites[2].setup.msgs[0]} Keyword 'Run Keyword If Test Passed' can only be used in test teardown. FAIL
- Check Log Message ${SUITE.suites[2].teardown.msgs[0]} Keyword 'Run Keyword If Test Passed' can only be used in test teardown. FAIL
+ Check Log Message ${SUITE.suites[2].setup[0]} Keyword 'Run Keyword If Test Passed' can only be used in test teardown. FAIL
+ Check Log Message ${SUITE.suites[2].teardown[0]} Keyword 'Run Keyword If Test Passed' can only be used in test teardown. FAIL
Variable Values Should Not Be Visible As Keyword's Arguments
${tc} = Check Test Case Run Keyword If Test Failed Uses User Keyword
- Check Keyword Data ${tc.teardown} BuiltIn.Run Keyword If Test Failed args=Teardown UK, \${TEARDOWN MESSAGE} type=TEARDOWN
- Check Keyword Data ${tc.teardown.kws[0]} Teardown UK args=\${TEARDOWN MESSAGE}
- Check Keyword Data ${tc.teardown.kws[0].kws[0]} BuiltIn.Log args=\${message}
+ Check Keyword Data ${tc.teardown} BuiltIn.Run Keyword If Test Failed args=Teardown UK, \${TEARDOWN MESSAGE} type=TEARDOWN
+ Check Keyword Data ${tc.teardown[0]} Teardown UK args=\${TEARDOWN MESSAGE}
+ Check Keyword Data ${tc.teardown[0, 0]} BuiltIn.Log args=\${message}
diff --git a/atest/robot/standard_libraries/builtin/run_keyword_if_unless.robot b/atest/robot/standard_libraries/builtin/run_keyword_if_unless.robot
index 07bc9abe674..5141a284b7e 100644
--- a/atest/robot/standard_libraries/builtin/run_keyword_if_unless.robot
+++ b/atest/robot/standard_libraries/builtin/run_keyword_if_unless.robot
@@ -8,21 +8,21 @@ ${EXECUTED} This is executed
*** Test Cases ***
Run Keyword If With True Expression
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.body[0].body[0].msgs[0]} ${EXECUTED}
+ Check Log Message ${tc[0, 0, 0]} ${EXECUTED}
Run Keyword If With False Expression
${tc} = Check Test Case ${TEST NAME}
- Should Be Empty ${tc.body[0].body}
+ Should Be Empty ${tc[0].body}
Run Keyword In User Keyword
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.body[0].body[0].body[0].msgs[0]} ${EXECUTED}
- Should Be Empty ${tc.body[1].body[0].body}
+ Check Log Message ${tc[0, 0, 0, 0]} ${EXECUTED}
+ Should Be Empty ${tc[1, 0].body}
Run Keyword With ELSE
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.body[1].body[0].msgs[0]} ${EXECUTED}
- Check Log Message ${tc.body[3].body[0].msgs[0]} ${EXECUTED}
+ Check Log Message ${tc[1, 0, 0]} ${EXECUTED}
+ Check Log Message ${tc[3, 0, 0]} ${EXECUTED}
Keyword Name in ELSE as variable
Check Test Case ${TEST NAME}
@@ -45,18 +45,18 @@ Only first ELSE is significant
Run Keyword With ELSE IF
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.body[1].body[0].msgs[0]} ${EXECUTED}
+ Check Log Message ${tc[1, 0, 0]} ${EXECUTED}
Run Keyword with ELSE IF and ELSE
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.body[0].body[0].msgs[0]} ${EXECUTED}
- Check Log Message ${tc.body[1].body[0].msgs[0]} ${EXECUTED}
+ Check Log Message ${tc[0, 0, 0]} ${EXECUTED}
+ Check Log Message ${tc[1, 0, 0]} ${EXECUTED}
Run Keyword with multiple ELSE IF
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.body[0].body[0].msgs[0]} ${EXECUTED}
- Check Log Message ${tc.body[1].body[0].msgs[0]} ${EXECUTED}
- Check Log Message ${tc.body[2].body[0].msgs[0]} ${EXECUTED}
+ Check Log Message ${tc[0, 0, 0]} ${EXECUTED}
+ Check Log Message ${tc[1, 0, 0]} ${EXECUTED}
+ Check Log Message ${tc[2, 0, 0]} ${EXECUTED}
Keyword Name in ELSE IF as variable
Check Test Case ${TEST NAME}
@@ -79,53 +79,53 @@ ELSE IF without keyword is invalid
ELSE before ELSE IF is ignored
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.body[0].body[0].msgs[0]} ${EXECUTED}
+ Check Log Message ${tc[0, 0, 0]} ${EXECUTED}
ELSE and ELSE IF inside list arguments should be escaped
Check Test Case ${TEST NAME}
ELSE and ELSE IF must be upper case
${tc} = Check Test Case ${TEST NAME}
- Test ELSE (IF) Escaping ${tc.body[0].body[0]} else
- Test ELSE (IF) Escaping ${tc.body[1].body[0]} ELSE iF
+ Test ELSE (IF) Escaping ${tc[0, 0]} else
+ Test ELSE (IF) Escaping ${tc[1, 0]} ELSE iF
ELSE and ELSE IF must be whitespace sensitive
${tc} = Check Test Case ${TEST NAME}
- Test ELSE (IF) Escaping ${tc.body[0].body[0]} EL SE
- Test ELSE (IF) Escaping ${tc.body[1].body[0]} ELSEIF
+ Test ELSE (IF) Escaping ${tc[0, 0]} EL SE
+ Test ELSE (IF) Escaping ${tc[1, 0]} ELSEIF
Run Keyword With Escaped ELSE and ELSE IF
${tc} = Check Test Case ${TEST NAME}
- Test ELSE (IF) Escaping ${tc.body[0].body[0]} ELSE
- Test ELSE (IF) Escaping ${tc.body[1].body[0]} ELSE IF
+ Test ELSE (IF) Escaping ${tc[0, 0]} ELSE
+ Test ELSE (IF) Escaping ${tc[1, 0]} ELSE IF
Run Keyword With ELSE and ELSE IF from Variable
${tc} = Check Test Case ${TEST NAME}
- Test ELSE (IF) Escaping ${tc.body[0].body[0]} ELSE
- Test ELSE (IF) Escaping ${tc.body[1].body[0]} ELSE
- Test ELSE (IF) Escaping ${tc.body[2].body[0]} ELSE IF
- Test ELSE (IF) Escaping ${tc.body[3].body[0]} ELSE IF
+ Test ELSE (IF) Escaping ${tc[0, 0]} ELSE
+ Test ELSE (IF) Escaping ${tc[1, 0]} ELSE
+ Test ELSE (IF) Escaping ${tc[2, 0]} ELSE IF
+ Test ELSE (IF) Escaping ${tc[3, 0]} ELSE IF
Run Keyword Unless With False Expression
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${ERRORS[0]} Keyword 'BuiltIn.Run Keyword Unless' is deprecated. WARN
- Check Log Message ${tc.body[1].body[0]} Keyword 'BuiltIn.Run Keyword Unless' is deprecated. WARN
- Check Log Message ${tc.body[1].body[1].msgs[0]} ${EXECUTED}
+ Check Log Message ${ERRORS[0]} Keyword 'BuiltIn.Run Keyword Unless' is deprecated. WARN
+ Check Log Message ${tc[1, 0]} Keyword 'BuiltIn.Run Keyword Unless' is deprecated. WARN
+ Check Log Message ${tc[1, 1, 0]} ${EXECUTED}
Run Keyword Unless With True Expression
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${ERRORS[1]} Keyword 'BuiltIn.Run Keyword Unless' is deprecated. WARN
- Check Log Message ${tc.body[0].body[0]} Keyword 'BuiltIn.Run Keyword Unless' is deprecated. WARN
- Length Should Be ${tc.body[0].body} 1
+ Check Log Message ${ERRORS[1]} Keyword 'BuiltIn.Run Keyword Unless' is deprecated. WARN
+ Check Log Message ${tc[0, 0]} Keyword 'BuiltIn.Run Keyword Unless' is deprecated. WARN
+ Length Should Be ${tc[0].body} 1
Variable Values Should Not Be Visible As Keyword's Arguments
${tc} = Check Test Case Run Keyword In User Keyword
- Check Keyword Data ${tc.body[0].body[0]} BuiltIn.Run Keyword If args='\${status}' == 'PASS', Log, \${message}
- Check Keyword Data ${tc.body[0].body[0].body[0]} BuiltIn.Log args=\${message}
+ Check Keyword Data ${tc[0, 0]} BuiltIn.Run Keyword If args='\${status}' == 'PASS', Log, \${message}
+ Check Keyword Data ${tc[0, 0, 0]} BuiltIn.Log args=\${message}
*** Keywords ***
Test ELSE (IF) Escaping
[Arguments] ${kw} ${else (if)}
- Length Should Be ${kw.msgs} 2
- Check Log Message ${kw.msgs[0]} ${else (if)}
- Check Log Message ${kw.msgs[1]} ${EXECUTED}
+ Length Should Be ${kw.body} 2
+ Check Log Message ${kw[0]} ${else (if)}
+ Check Log Message ${kw[1]} ${EXECUTED}
diff --git a/atest/robot/standard_libraries/builtin/run_keyword_variants_variable_handling.robot b/atest/robot/standard_libraries/builtin/run_keyword_variants_variable_handling.robot
index 472e828434a..632d23d74a1 100644
--- a/atest/robot/standard_libraries/builtin/run_keyword_variants_variable_handling.robot
+++ b/atest/robot/standard_libraries/builtin/run_keyword_variants_variable_handling.robot
@@ -5,18 +5,18 @@ Resource atest_resource.robot
*** Test Cases ***
Variable Values Should Not Be Visible As Keyword's Arguments
${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.kws[0]} BuiltIn.Run Keyword args=My UK, Log, \${OBJECT}
- Check Keyword Data ${tc.kws[0].kws[0]} My UK args=Log, \${OBJECT}
- Check Keyword Data ${tc.kws[0].kws[0].kws[0]} BuiltIn.Run Keyword args=\${name}, \@{args}
- Check Keyword Data ${tc.kws[0].kws[0].kws[0].kws[0]} BuiltIn.Log args=\@{args}
- Check Log Message ${tc.kws[0].kws[0].kws[0].kws[0].msgs[0]} Robot
- Check Keyword Data ${tc.kws[0].kws[0].kws[1].kws[0]} BuiltIn.Log args=\${args}[0]
- Check Log Message ${tc.kws[0].kws[0].kws[1].kws[0].msgs[0]} Robot
+ Check Keyword Data ${tc[0]} BuiltIn.Run Keyword args=My UK, Log, \${OBJECT}
+ Check Keyword Data ${tc[0, 0]} My UK args=Log, \${OBJECT}
+ Check Keyword Data ${tc[0, 0, 0]} BuiltIn.Run Keyword args=\${name}, \@{args}
+ Check Keyword Data ${tc[0, 0, 0, 0]} BuiltIn.Log args=\@{args}
+ Check Log Message ${tc[0, 0, 0, 0, 0]} Robot
+ Check Keyword Data ${tc[0, 0, 1, 0]} BuiltIn.Log args=\${args}[0]
+ Check Log Message ${tc[0, 0, 1, 0, 0]} Robot
Run Keyword When Keyword and Arguments Are in List Variable
${tc} = Check Test Case ${TEST NAME}
- Check Keyword Data ${tc.kws[0].kws[0]} \\Log Many args=c:\\\\temp\\\\foo, \\\${notvar}
- Check Keyword Data ${tc.kws[1].kws[0]} \\Log Many args=\\\${notvar}
+ Check Keyword Data ${tc[0, 0]} \\Log Many args=c:\\\\temp\\\\foo, \\\${notvar}
+ Check Keyword Data ${tc[1, 0]} \\Log Many args=\\\${notvar}
Run Keyword With Empty List Variable
Check Test Case ${TEST NAME}
@@ -57,7 +57,7 @@ Run Keyword If With List And One Argument That needs to Be Processed
*** Keywords ***
Check Keyword Arguments And Messages
[Arguments] ${tc}
- Check Keyword Data ${tc.kws[0].kws[0]} \\Log Many args=\@{ARGS}
- Check Keyword Data ${tc.kws[0].kws[0].kws[0]} BuiltIn.Log Many args=\@{args}
- Check Log Message ${tc.kws[0].kws[0].kws[0].msgs[0]} c:\\temp\\foo
- Check Log Message ${tc.kws[0].kws[0].kws[0].msgs[1]} \${notvar}
+ Check Keyword Data ${tc[0, 0]} \\Log Many args=\@{ARGS}
+ Check Keyword Data ${tc[0, 0, 0]} BuiltIn.Log Many args=\@{args}
+ Check Log Message ${tc[0, 0, 0, 0]} c:\\temp\\foo
+ Check Log Message ${tc[0, 0, 0, 1]} \${notvar}
diff --git a/atest/robot/standard_libraries/builtin/run_keyword_variants_with_escaping_control_arguments.robot b/atest/robot/standard_libraries/builtin/run_keyword_variants_with_escaping_control_arguments.robot
index bf6a2754738..6c55e2821a3 100644
--- a/atest/robot/standard_libraries/builtin/run_keyword_variants_with_escaping_control_arguments.robot
+++ b/atest/robot/standard_libraries/builtin/run_keyword_variants_with_escaping_control_arguments.robot
@@ -5,32 +5,32 @@ Resource atest_resource.robot
*** Test Cases ***
Run Keyword with Run Keywords with Arguments Inside List variable should escape AND
${tc}= Test Should Have Correct Keywords BuiltIn.Run Keywords
- Check Log Message ${tc.kws[0].kws[0].kws[0].kws[0].msgs[0]} log message
+ Check Log Message ${tc[0, 0, 0, 0, 0]} log message
Run Keyword with Run Keywords and Arguments Inside List variable should escape AND
${tc}= Test Should Have Correct Keywords BuiltIn.Run Keywords
- Check Log Message ${tc.kws[0].kws[0].kws[0].kws[0].msgs[0]} log message
+ Check Log Message ${tc[0, 0, 0, 0, 0]} log message
Run Keyword If with Run Keywords With Arguments Inside List variable should escape AND
${tc}= Test Should Have Correct Keywords BuiltIn.Run Keywords
- Check Log Message ${tc.kws[0].kws[0].kws[0].kws[0].msgs[0]} log message
+ Check Log Message ${tc[0, 0, 0, 0, 0]} log message
Run Keyword If with Run Keywords And Arguments Inside List variable should escape AND
${tc}= Test Should Have Correct Keywords BuiltIn.Run Keyword
- Check Log Message ${tc.kws[0].kws[0].kws[0].kws[0].kws[0].msgs[0]} log message
+ Check Log Message ${tc[0, 0, 0, 0, 0, 0]} log message
Run Keywords With Run Keyword If should not escape ELSE and ELSE IF
${tc}= Test Should Have Correct Keywords
... BuiltIn.Run Keyword If BuiltIn.Log BuiltIn.Run Keyword If
- Check Log Message ${tc.kws[0].kws[0].kws[0].msgs[0]} log message
- Check Log Message ${tc.kws[0].kws[1].msgs[0]} that
+ Check Log Message ${tc[0, 0, 0, 0]} log message
+ Check Log Message ${tc[0, 1, 0]} that
Run Keywords With Run Keyword If In List Variable Should Escape ELSE and ELSE IF From List Variable
${tc}= Test Should Have Correct Keywords
... BuiltIn.Run Keyword If BuiltIn.Log BuiltIn.Run Keyword If
- Check Log Message ${tc.kws[0].kws[1].msgs[0]} that
+ Check Log Message ${tc[0, 1, 0]} that
Run Keywords With Run Keyword If With Arguments From List Variable should escape ELSE and ELSE IF From List Variable
${tc}= Test Should Have Correct Keywords
... BuiltIn.Run Keyword If BuiltIn.Log BuiltIn.Run Keyword If
- Check Log Message ${tc.kws[0].kws[1].msgs[0]} that
+ Check Log Message ${tc[0, 1, 0]} that
diff --git a/atest/robot/standard_libraries/builtin/run_keyword_with_errors.robot b/atest/robot/standard_libraries/builtin/run_keyword_with_errors.robot
index dbd75652954..977d82e9da3 100644
--- a/atest/robot/standard_libraries/builtin/run_keyword_with_errors.robot
+++ b/atest/robot/standard_libraries/builtin/run_keyword_with_errors.robot
@@ -5,13 +5,13 @@ Resource atest_resource.robot
*** Test Cases ***
Ignore Error When Keyword Passes
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} My message
+ Check Log Message ${tc[0, 0, 0]} My message
Ignore Error When Keyword Fails
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} My error message FAIL
- Should Be Equal ${tc.kws[0].kws[0].status} FAIL
- Should Be Equal ${tc.kws[0].status} PASS
+ Should Be Equal ${tc[0].status} PASS
+ Should Be Equal ${tc[0, 0].status} FAIL
+ Check Log Message ${tc[0, 0, 0]} My error message FAIL
Ignore Error Returns When Keyword Passes
Check Test Case ${TEST NAME}
@@ -21,22 +21,22 @@ Ignore Error Returns When Keyword Fails
Ignore Error With User Keyword When Keywords Pass
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].kws[0].kws[0].msgs[0]} Hello world
- Check Keyword Data ${tc.kws[0].kws[0].kws[2]} BuiltIn.Evaluate \${ret} 1+2
+ Check Log Message ${tc[0, 0, 0, 0]} Hello world
+ Check Keyword Data ${tc[0, 0, 2]} BuiltIn.Evaluate \${ret} 1+2
Ignore Error With User Keyword When Keyword Fails
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].kws[0].kws[0].kws[0].msgs[0]} Hello world
- Check Log Message ${tc.kws[0].kws[0].kws[1].msgs[0]} Expected failure in UK FAIL
- Length Should Be ${tc.kws[0].kws[0].kws} 3
- Should Be Equal ${tc.kws[0].kws[0].kws[-1].status} NOT RUN
+ Check Log Message ${tc[0, 0, 0, 0, 0]} Hello world
+ Check Log Message ${tc[0, 0, 1, 0]} Expected failure in UK FAIL
+ Length Should Be ${tc[0, 0].body} 3
+ Should Be Equal ${tc[0, 0, -1].status} NOT RUN
Ignore Error With Arguments That Needs To Be Escaped
Check Test Case ${TEST NAME}
Ignore Error When Timeout Occurs
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.kws[0].status} FAIL Run Keyword And Ignore Error captured timeout even though it should not no values
+ Should Be Equal ${tc[0].status} FAIL Run Keyword And Ignore Error captured timeout even though it should not no values
Ignore Error When Timeout Occurs In UK
Check Test Case ${TEST NAME}
@@ -75,9 +75,9 @@ Ignore Error With "Passing" Exceptions
Expect Error When Error Occurs
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} My error message FAIL
- Should Be Equal ${tc.kws[0].kws[0].status} FAIL
- Should Be Equal ${tc.kws[0].status} PASS
+ Should Be Equal ${tc[0].status} PASS
+ Should Be Equal ${tc[0, 0].status} FAIL
+ Check Log Message ${tc[0, 0, 0]} My error message FAIL
Expect Error When Different Error Occurs
Check Test Case ${TEST NAME}
@@ -97,24 +97,24 @@ Expected Error Should Be Returned
Expect Error With User Keyword When Keywords Pass
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].kws[0].kws[0].msgs[0]} Hello world
- Check Keyword Data ${tc.kws[0].kws[0].kws[2]} BuiltIn.Evaluate \${ret} 1+2
+ Check Log Message ${tc[0, 0, 0, 0]} Hello world
+ Check Keyword Data ${tc[0, 0, 2]} BuiltIn.Evaluate \${ret} 1+2
Expect Error With User Keyword When Keyword Fails
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].kws[0].kws[0].kws[0].msgs[0]} Hello world
- Check Log Message ${tc.kws[0].kws[0].kws[1].msgs[0]} Expected failure in UK FAIL
- Length Should Be ${tc.kws[0].kws[0].kws} 3
- Should Be Equal ${tc.kws[0].kws[0].kws[-1].status} NOT RUN
+ Check Log Message ${tc[0, 0, 0, 0, 0]} Hello world
+ Check Log Message ${tc[0, 0, 1, 0]} Expected failure in UK FAIL
+ Length Should Be ${tc[0, 0].body} 3
+ Should Be Equal ${tc[0, 0, -1].status} NOT RUN
Expect Error With Arguments That Needs To Be Escaped
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[1].kws[0].msgs[0]} c:\\temp\\foo\\not_new_line
- Check Log Message ${tc.kws[1].kws[0].msgs[1]} \${notvar}
+ Check Log Message ${tc[1, 0, 0]} c:\\temp\\foo\\not_new_line
+ Check Log Message ${tc[1, 0, 1]} \${notvar}
Expect Error When Timeout Occurs
${tc} = Check Test Case ${TEST NAME}
- Should Be Equal ${tc.kws[0].status} FAIL Run Keyword And Expect Error captured timeout even though it should not no values
+ Should Be Equal ${tc[0].status} FAIL Run Keyword And Expect Error captured timeout even though it should not no values
Expect Error When Timeout Occurs In UK
Check Test Case ${TEST NAME}
@@ -171,4 +171,4 @@ Expect Error With "Passing" Exceptions
Variable Values Should Not Be Visible As Keyword's Arguments
${tc} = Check Test Case Ignore Error With Arguments That Needs To be Escaped
- Check Keyword Data ${tc.kws[3].kws[0]} BuiltIn.Create List args=\@{NEEDS ESCAPING}
+ Check Keyword Data ${tc[3, 0]} BuiltIn.Create List args=\@{NEEDS ESCAPING}
diff --git a/atest/robot/standard_libraries/builtin/run_keywords.robot b/atest/robot/standard_libraries/builtin/run_keywords.robot
index 307cb414be9..e245b9146e7 100644
--- a/atest/robot/standard_libraries/builtin/run_keywords.robot
+++ b/atest/robot/standard_libraries/builtin/run_keywords.robot
@@ -8,7 +8,7 @@ Resource atest_resource.robot
Passing keywords
${tc} = Test Should Have Correct Keywords
... BuiltIn.No Operation Passing BuiltIn.Log Variables
- Check Log Message ${tc.kws[0].kws[1].kws[0].msgs[0]} Hello, world!
+ Check Log Message ${tc[0, 1, 0, 0]} Hello, world!
Failing keyword
Test Should Have Correct Keywords
@@ -17,18 +17,18 @@ Failing keyword
Embedded arguments
${tc} = Test Should Have Correct Keywords
... Embedded "arg" Embedded "\${1}" Embedded object "\${OBJECT}"
- Check Log Message ${tc.kws[0].kws[0].kws[0].msgs[0]} arg
- Check Log Message ${tc.kws[0].kws[1].kws[0].msgs[0]} 1
- Check Log Message ${tc.kws[0].kws[2].kws[0].msgs[0]} Robot
+ Check Log Message ${tc[0, 0, 0, 0]} arg
+ Check Log Message ${tc[0, 1, 0, 0]} 1
+ Check Log Message ${tc[0, 2, 0, 0]} Robot
Embedded arguments with library keywords
${tc} = Test Should Have Correct Keywords
... embedded_args.Embedded "arg" in library
... embedded_args.Embedded "\${1}" in library
... embedded_args.Embedded object "\${OBJECT}" in library
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} arg
- Check Log Message ${tc.kws[0].kws[1].msgs[0]} 1
- Check Log Message ${tc.kws[0].kws[2].msgs[0]} Robot
+ Check Log Message ${tc[0, 0, 0]} arg
+ Check Log Message ${tc[0, 1, 0]} 1
+ Check Log Message ${tc[0, 2, 0]} Robot
Keywords names needing escaping
Test Should Have Correct Keywords
@@ -80,7 +80,7 @@ In test teardown with ExecutionPassed exception after continuable failure
Check Test Case ${TESTNAME}
In suite setup
- Check Log Message ${SUITE.setup.kws[0].kws[0].msgs[0]} Hello, world!
+ Check Log Message ${SUITE.setup[0, 0, 0]} Hello, world!
Should Contain Keywords ${SUITE.setup} Passing BuiltIn.No Operation
In suite teardown
diff --git a/atest/robot/standard_libraries/builtin/run_keywords_with_arguments.robot b/atest/robot/standard_libraries/builtin/run_keywords_with_arguments.robot
index b155ef8b66d..4e792da98bf 100644
--- a/atest/robot/standard_libraries/builtin/run_keywords_with_arguments.robot
+++ b/atest/robot/standard_libraries/builtin/run_keywords_with_arguments.robot
@@ -7,46 +7,46 @@ Resource atest_resource.robot
*** Test Cases ***
With arguments
${tc}= Test Should Have Correct Keywords BuiltIn.Should Be Equal BuiltIn.No Operation BuiltIn.Log Many BuiltIn.Should Be Equal
- Check Log Message ${tc.kws[0].kws[2].msgs[1]} 1
+ Check Log Message ${tc[0, 2, 1]} 1
Should fail with failing keyword
Test Should Have Correct Keywords BuiltIn.No Operation BuiltIn.Should Be Equal
Should support keywords and arguments from variables
${tc}= Test Should Have Correct Keywords BuiltIn.Should Be Equal BuiltIn.No Operation BuiltIn.Log Many BuiltIn.Should Be Equal As Integers
- Check Log Message ${tc.kws[0].kws[2].msgs[0]} hello
- Check Log Message ${tc.kws[0].kws[2].msgs[1]} 1
- Check Log Message ${tc.kws[0].kws[2].msgs[2]} 2
- Check Log Message ${tc.kws[0].kws[2].msgs[3]} 3
+ Check Log Message ${tc[0, 2, 0]} hello
+ Check Log Message ${tc[0, 2, 1]} 1
+ Check Log Message ${tc[0, 2, 2]} 2
+ Check Log Message ${tc[0, 2, 3]} 3
AND must be upper case
${tc}= Test Should Have Correct Keywords BuiltIn.Log Many no kw
- Check Log Message ${tc.kws[0].kws[0].msgs[1]} and
+ Check Log Message ${tc[0, 0, 1]} and
AND must be whitespace sensitive
${tc}= Test Should Have Correct Keywords BuiltIn.Log Many no kw
- Check Log Message ${tc.kws[0].kws[0].msgs[1]} A ND
+ Check Log Message ${tc[0, 0, 1]} A ND
Escaped AND
${tc}= Test Should Have Correct Keywords BuiltIn.Log Many no kw
- Check Log Message ${tc.kws[0].kws[0].msgs[1]} AND
+ Check Log Message ${tc[0, 0, 1]} AND
AND from Variable
${tc}= Test Should Have Correct Keywords BuiltIn.Log Many no kw
- Check Log Message ${tc.kws[0].kws[0].msgs[1]} AND
+ Check Log Message ${tc[0, 0, 1]} AND
AND in List Variable
${tc}= Test Should Have Correct Keywords BuiltIn.Log Many no kw
- Check Log Message ${tc.kws[0].kws[0].msgs[1]} AND
+ Check Log Message ${tc[0, 0, 1]} AND
Escapes in List Variable should be handled correctly
${tc}= Test Should Have Correct Keywords BuiltIn.Log Many no kw
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} 1
- Check Log Message ${tc.kws[0].kws[0].msgs[1]} AND
- Check Log Message ${tc.kws[0].kws[0].msgs[2]} 2
- Check Log Message ${tc.kws[0].kws[0].msgs[3]} Log Many
- Check Log Message ${tc.kws[0].kws[0].msgs[4]} x\${escaped}
- Check Log Message ${tc.kws[0].kws[0].msgs[5]} c:\\temp
+ Check Log Message ${tc[0, 0, 0]} 1
+ Check Log Message ${tc[0, 0, 1]} AND
+ Check Log Message ${tc[0, 0, 2]} 2
+ Check Log Message ${tc[0, 0, 3]} Log Many
+ Check Log Message ${tc[0, 0, 4]} x\${escaped}
+ Check Log Message ${tc[0, 0, 5]} c:\\temp
AND as last argument should raise an error
Test Should Have Correct Keywords BuiltIn.Log Many BuiltIn.No Operation
diff --git a/atest/robot/standard_libraries/builtin/set_documentation.robot b/atest/robot/standard_libraries/builtin/set_documentation.robot
index d155f09b210..bbe36cbe526 100644
--- a/atest/robot/standard_libraries/builtin/set_documentation.robot
+++ b/atest/robot/standard_libraries/builtin/set_documentation.robot
@@ -5,35 +5,40 @@ Resource atest_resource.robot
*** Test Cases ***
Set test documentation
${tc} = Check Test Doc ${TESTNAME} This has been set!\nTo several lines.
- Check Log Message ${tc.kws[0].msgs[0]} Set test documentation to:\nThis has been set!\nTo several lines.
+ Check Log Message ${tc[0, 0]} Set test documentation to:\nThis has been set!\nTo several lines.
Replace test documentation
${tc} = Check Test Doc ${TESTNAME} New doc
- Check Log Message ${tc.kws[0].msgs[0]} Set test documentation to:\nNew doc
+ Check Log Message ${tc[0, 0]} Set test documentation to:\nNew doc
Append to test documentation
- ${tc} = Check Test Doc ${TESTNAME} Original doc is continued \n\ntwice!
- Check Log Message ${tc.kws[0].msgs[0]} Set test documentation to:\nOriginal doc is continued
- Check Log Message ${tc.kws[2].msgs[0]} Set test documentation to:\nOriginal doc is continued \n\ntwice!
+ ${tc} = Check Test Doc ${TESTNAME} Original doc is continued \n\ntwice! thrice!!
+ Check Log Message ${tc[0, 0]} Set test documentation to:\nOriginal doc is continued
+ Check Log Message ${tc[2, 0]} Set test documentation to:\nOriginal doc is continued \n\ntwice!
+ Check Log Message ${tc[4, 0]} Set test documentation to:\nOriginal doc is continued \n\ntwice! thrice
+ Check Log Message ${tc[6, 0]} Set test documentation to:\nOriginal doc is continued \n\ntwice! thrice!
+ Check Log Message ${tc[8, 0]} Set test documentation to:\nOriginal doc is continued \n\ntwice! thrice!!
Set suite documentation
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} Set suite documentation to:\nNew suite doc
+ Check Log Message ${tc[0, 0]} Set suite documentation to:\nNew suite doc
Check Test Case ${TESTNAME} 2
Should Start With ${SUITE.suites[0].doc} New suite doc
Append to suite documentation
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} Set suite documentation to:\nNew suite doc is continued
+ Check Log Message ${tc[0, 0]} Set suite documentation to:\nNew suite doc is continued
${tc} = Check Test Case ${TESTNAME} 2
- Check Log Message ${tc.kws[1].msgs[0]} Set suite documentation to:\nNew suite doc is continued \n\ntwice!
- Should Be Equal ${SUITE.suites[0].doc} New suite doc is continued \n\ntwice!
+ Check Log Message ${tc[1, 0]} Set suite documentation to:\nNew suite doc is continued \n\ntwice!
+ Check Log Message ${tc[3, 0]} Set suite documentation to:\nNew suite doc is continued \n\ntwice!,thrice
+ Check Log Message ${tc[5, 0]} Set suite documentation to:\nNew suite doc is continued \n\ntwice!,thrice?1
+ Should Be Equal ${SUITE.suites[0].doc} New suite doc is continued \n\ntwice!,thrice?1
Set init file suite docs
Should Be Equal ${SUITE.doc} Init file doc. Concatenated in setup. Appended in test.
- Check Log Message ${SUITE.setup.msgs[0]} Set suite documentation to:\nInit file doc. Concatenated in setup.
+ Check Log Message ${SUITE.setup[0]} Set suite documentation to:\nInit file doc. Concatenated in setup.
Set top level suite documentation
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} Set suite documentation to:\nInit file doc. Concatenated in setup. Appended in test.
+ Check Log Message ${tc[0, 0]} Set suite documentation to:\nInit file doc. Concatenated in setup. Appended in test.
diff --git a/atest/robot/standard_libraries/builtin/set_library_search_order.robot b/atest/robot/standard_libraries/builtin/set_library_search_order.robot
index bb9d316a7f0..f7b692bbc7c 100644
--- a/atest/robot/standard_libraries/builtin/set_library_search_order.robot
+++ b/atest/robot/standard_libraries/builtin/set_library_search_order.robot
@@ -41,6 +41,6 @@ Library Search Order Is Case Insensitive
Search Order Controlled Match Containing Embedded Arguments Wins Over Exact Match
Check Test Case ${TEST NAME}
-
+
Best Search Order Controlled Match Wins In Library
Check Test Case ${TEST NAME}
diff --git a/atest/robot/standard_libraries/builtin/set_log_level.robot b/atest/robot/standard_libraries/builtin/set_log_level.robot
index bc836e09f84..621e617125c 100644
--- a/atest/robot/standard_libraries/builtin/set_log_level.robot
+++ b/atest/robot/standard_libraries/builtin/set_log_level.robot
@@ -5,35 +5,35 @@ Resource atest_resource.robot
*** Test Cases ***
Set Log Level
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} Log level changed from INFO to TRACE. DEBUG
- Check Log Message ${tc.kws[1].msgs[1]} This is logged TRACE
- Check Log Message ${tc.kws[2].msgs[1]} This is logged DEBUG
- Check Log Message ${tc.kws[3].msgs[1]} This is logged INFO
- Check Log Message ${tc.kws[4].msgs[1]} Log level changed from TRACE to DEBUG. DEBUG
- Should Be Empty ${tc.kws[6].msgs}
- Check Log Message ${tc.kws[7].msgs[0]} This is logged DEBUG
- Check Log Message ${tc.kws[8].msgs[0]} This is logged INFO
- Should Be Empty ${tc.kws[9].msgs}
- Should Be Empty ${tc.kws[10].msgs}
- Should Be Empty ${tc.kws[11].msgs}
- Check Log Message ${tc.kws[12].msgs[0]} This is logged INFO
- Should Be Empty ${tc.kws[15].msgs}
- Check Log Message ${tc.kws[16].msgs[0]} This is logged ERROR
- Should Be Empty ${tc.kws[17].msgs}
- Should Be Empty ${tc.kws[18].msgs}
- Should Be Empty ${tc.kws[19].msgs}
+ Check Log Message ${tc[0, 0]} Log level changed from INFO to TRACE. DEBUG
+ Check Log Message ${tc[1, 1]} This is logged TRACE
+ Check Log Message ${tc[2, 1]} This is logged DEBUG
+ Check Log Message ${tc[3, 1]} This is logged INFO
+ Check Log Message ${tc[4, 1]} Log level changed from TRACE to DEBUG. DEBUG
+ Should Be Empty ${tc[6].body}
+ Check Log Message ${tc[7, 0]} This is logged DEBUG
+ Check Log Message ${tc[8, 0]} This is logged INFO
+ Should Be Empty ${tc[9].body}
+ Should Be Empty ${tc[10].body}
+ Should Be Empty ${tc[11].body}
+ Check Log Message ${tc[12, 0]} This is logged INFO
+ Should Be Empty ${tc[15].body}
+ Check Log Message ${tc[16, 0]} This is logged ERROR
+ Should Be Empty ${tc[17].body}
+ Should Be Empty ${tc[18].body}
+ Should Be Empty ${tc[19].body}
Invalid Log Level Failure Is Catchable
Check Test Case ${TESTNAME}
Reset Log Level
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} Log level changed from INFO to DEBUG. DEBUG
- Check Log Message ${tc.kws[1].msgs[0]} This is logged INFO
- Check Log Message ${tc.kws[2].msgs[0]} This is logged DEBUG
- Should Be Empty ${tc.kws[3].msgs}
- Check Log Message ${tc.kws[4].msgs[0]} This is logged INFO
- Should Be Empty ${tc.kws[5].msgs}
+ Check Log Message ${tc[0, 0]} Log level changed from INFO to DEBUG. DEBUG
+ Check Log Message ${tc[1, 0]} This is logged INFO
+ Check Log Message ${tc[2, 0]} This is logged DEBUG
+ Should Be Empty ${tc[3].body}
+ Check Log Message ${tc[4, 0]} This is logged INFO
+ Should Be Empty ${tc[5].body}
Log Level Goes To HTML
File Should Contain ${OUTDIR}${/}set_log_level_log.html KW Info to log
diff --git a/atest/robot/standard_libraries/builtin/set_suite_metadata.robot b/atest/robot/standard_libraries/builtin/set_suite_metadata.robot
index 5170c9f8c06..36ca3731efc 100644
--- a/atest/robot/standard_libraries/builtin/set_suite_metadata.robot
+++ b/atest/robot/standard_libraries/builtin/set_suite_metadata.robot
@@ -5,49 +5,62 @@ Resource atest_resource.robot
*** Test Cases ***
Set new value
Metadata should have value New metadata Set in test
- ${tc} = Check test case ${TESTNAME}
- Check log message ${tc.kws[0].msgs[0]}
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Log Message ${tc[0, 0]}
... Set suite metadata 'New metadata' to value 'Set in test'.
Override existing value
Metadata should have value Initial New value
- ${tc} = Check test case ${TESTNAME}
- Check log message ${tc.kws[0].msgs[0]}
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Log Message ${tc[0, 0]}
... Set suite metadata 'Initial' to value 'New value'.
Names are case and space insensitive
Metadata should have value My Name final value
- ${tc} = Check test case ${TESTNAME}
- Check log message ${tc.kws[1].msgs[0]}
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Log Message ${tc[1, 0]}
... Set suite metadata 'MYname' to value 'final value'.
Append to value
Metadata should have value To Append Original is continued \n\ntwice!
- ${tc} = Check test case ${TESTNAME}
- Check log message ${tc.kws[0].msgs[0]}
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Log Message ${tc[0, 0]}
... Set suite metadata 'To Append' to value 'Original'.
- Check log message ${tc.kws[2].msgs[0]}
+ Check Log Message ${tc[2, 0]}
... Set suite metadata 'toappend' to value 'Original is continued'.
- Check log message ${tc.kws[4].msgs[0]}
+ Check Log Message ${tc[4, 0]}
... Set suite metadata 'TOAPPEND' to value 'Original is continued \n\ntwice!'.
+ Check Log Message ${tc[6, 0]}
+ ... Set suite metadata 'Version' to value '1.0'.
+ Check Log Message ${tc[8, 0]}
+ ... Set suite metadata 'version' to value '1.0/2.0'.
+ Check Log Message ${tc[10, 0]}
+ ... Set suite metadata 'ver sion' to value '1.0/2.0/3.0'.
Set top-level suite metadata
Metadata should have value New metadata Metadata for top level suite top=yes
- ${tc} = Check test case ${TESTNAME}
- Check log message ${tc.kws[0].msgs[0]}
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Log Message ${tc[0, 0]}
... Set suite metadata 'New metadata' to value 'Metadata for'.
- Check log message ${tc.kws[1].msgs[0]}
+ Check Log Message ${tc[1, 0]}
... Set suite metadata 'newmetadata' to value 'Metadata for top level suite'.
+ Metadata should have value Separator 2top**level top=yes
+ Check Log Message ${tc[3, 0]}
+ ... Set suite metadata 'Separator' to value '2'.
+ Check Log Message ${tc[4, 0]}
+ ... Set suite metadata 'Separator' to value '2top'.
+ Check Log Message ${tc[5, 0]}
+ ... Set suite metadata 'Separator' to value '2top**level'.
Non-ASCII and non-string names and values
- ${tc} = Check test case ${TESTNAME}
- Check log message ${tc.kws[0].msgs[0]}
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Log Message ${tc[0, 0]}
... Set suite metadata '42' to value '1'.
- Check log message ${tc.kws[2].msgs[0]}
+ Check Log Message ${tc[2, 0]}
... Set suite metadata '42' to value '1 päivä'.
Modifying \${SUITE METADATA} has no effect also after setting metadata
- Check test case ${TESTNAME}
+ Check Test Case ${TESTNAME}
Metadata should have value Cannot be set otherwise
Set in suite setup
diff --git a/atest/robot/standard_libraries/builtin/set_test_message.robot b/atest/robot/standard_libraries/builtin/set_test_message.robot
index 28882ff74d7..bb79583aa1e 100644
--- a/atest/robot/standard_libraries/builtin/set_test_message.robot
+++ b/atest/robot/standard_libraries/builtin/set_test_message.robot
@@ -6,42 +6,44 @@ Resource atest_resource.robot
*** Test Cases ***
Set Message To Successful Test
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} Set test message to:\nMy Test
+ Check Log Message ${tc[0, 0]} Set test message to:\nMy Test
Reset Message
Check Test Case ${TEST NAME}
Append To Message
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} Set test message to:\nMy
- Check Log Message ${tc.kws[1].msgs[0]} Set test message to:\nMy & its continuation <>
+ Check Log Message ${tc[0, 0]} Set test message to:\nMy
+ Check Log Message ${tc[1, 0]} Set test message to:\nMy & its continuation <>
+ Check Log Message ${tc[2, 0]} Set test message to:\nMy & its continuation <>1
+ Check Log Message ${tc[3, 0]} Set test message to:\nMy & its continuation <>1,\n2
Set Non-ASCII Message
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} Set test message to:\nHyvää yötä
+ Check Log Message ${tc[0, 0]} Set test message to:\nHyvää yötä
Set Multiline Message
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} Set test message to:\n1\n2\n3
+ Check Log Message ${tc[0, 0]} Set test message to:\n1\n2\n3
Set HTML Message
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} Set test message to:\nMy HTML message html=True
+ Check Log Message ${tc[0, 0]} Set test message to:\nMy HTML message html=True
Append HTML to non-HTML
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} Set test message to:\nMy non-HTML & html=False
- Check Log Message ${tc.kws[1].msgs[0]} Set test message to:\nMy non-HTML <message> & its HTML continuation html=True
+ Check Log Message ${tc[0, 0]} Set test message to:\nMy non-HTML & html=False
+ Check Log Message ${tc[1, 0]} Set test message to:\nMy non-HTML <message> & its HTML continuation html=True
Append non-HTML to HTML
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} Set test message to:\nMy HTML message html=True
- Check Log Message ${tc.kws[1].msgs[0]} Set test message to:\nMy HTML message & its non-HTML <continuation> html=True
+ Check Log Message ${tc[0, 0]} Set test message to:\nMy HTML message html=True
+ Check Log Message ${tc[1, 0]} Set test message to:\nMy HTML message & its non-HTML <continuation> html=True
Append HTML to HTML
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} Set test message to:\nMy HTML message html=True
- Check Log Message ${tc.kws[1].msgs[0]} Set test message to:\nMy HTML message & its HTML continuation html=True
+ Check Log Message ${tc[0, 0]} Set test message to:\nMy HTML message html=True
+ Check Log Message ${tc[1, 0]} Set test message to:\nMy HTML message & its HTML continuation html=True
Set Non-String Message
Check Test Case ${TEST NAME}
@@ -87,3 +89,18 @@ Not Allowed In Suite Setup or Teardown
... Also suite teardown failed:
... 'Set Test Message' keyword cannot be used in suite setup or teardown.
Should Be Equal ${SUITE.suites[1].message} ${error}
+
+Append HTML to non-HTML with separator
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Log Message ${tc[0, 0]} Set test message to:\nA non HTML html=False
+ Check Log Message ${tc[1, 0]} Set test message to:\nA non HTML <message>&its HTML continuation html=True
+
+Append non-HTML to HTML with separator
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Log Message ${tc[0, 0]} Set test message to:\nA HTML message html=True
+ Check Log Message ${tc[1, 0]} Set test message to:\nA HTML message<\br>its non-HTML <continuation> html=True
+
+Append HTML to HTML with separator
+ ${tc} = Check Test Case ${TEST NAME}
+ Check Log Message ${tc[0, 0]} Set test message to:\nA HTML message html=True
+ Check Log Message ${tc[1, 0]} Set test message to:\nA HTML message && its HTML continuation html=True
diff --git a/atest/robot/standard_libraries/builtin/setting_variables.robot b/atest/robot/standard_libraries/builtin/setting_variables.robot
index d1e11227a76..18aa6b8d2b0 100644
--- a/atest/robot/standard_libraries/builtin/setting_variables.robot
+++ b/atest/robot/standard_libraries/builtin/setting_variables.robot
@@ -8,23 +8,23 @@ Resource atest_resource.robot
*** Test Cases ***
Set Variable
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} \${var} = Hello
+ Check Log Message ${tc[0, 0]} \${var} = Hello
Set Variable With More Or Less Than One Value
Check Test Case ${TESTNAME}
Set Local Variable - Scalars
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[1].msgs[0]} \${scalar} = Hello world
+ Check Log Message ${tc[1, 0]} \${scalar} = Hello world
Set Local Variable - Lists
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[3].msgs[0]} \@{list} = [ One | Two | Three ]
- Check Log Message ${tc.kws[6].msgs[0]} \@{list} = [ 1 | 2 | 3 ]
+ Check Log Message ${tc[3, 0]} \@{list} = [ One | Two | Three ]
+ Check Log Message ${tc[6, 0]} \@{list} = [ 1 | 2 | 3 ]
Set Local Variable - Dicts
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[4].msgs[0]} \&{DICT} = { a=1 | 2=b }
+ Check Log Message ${tc[4, 0]} \&{DICT} = { a=1 | 2=b }
Set Local Variables Overrides Test Variables
Check Test Case ${TESTNAME}
@@ -56,7 +56,7 @@ Set Test Variable Needing Escaping
Set Test Variable Affect Subsequent Keywords
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.kws[0].doc} Makes a variable available everywhere within the scope of the current test.
+ Should Be Equal ${tc[0].doc} Makes a variable available everywhere within the scope of the current test.
Set Test Variable In User Keyword
Check Test Case ${TESTNAME}
@@ -67,12 +67,18 @@ Set Test Variable Not Affecting Other Tests
Test Variables Set In One Suite Are Not Available In Another
Check Test Case ${TESTNAME}
-Set Test Variable cannot be used in suite setup or teardown
+Test variables set on suite level is not seen in tests
+ Check Test Case ${TESTNAME}
+
+Test variable set on suite level does not hide existing suite variable
+ Check Test Case ${TESTNAME}
+
+Test variable set on suite level can be overridden as suite variable
Check Test Case ${TESTNAME}
Set Task Variable as alias for Set Test Variable
${tc} = Check Test Case ${TESTNAME}
- Should Be Equal ${tc.kws[0].doc} Makes a variable available everywhere within the scope of the current task.
+ Should Be Equal ${tc[0].doc} Makes a variable available everywhere within the scope of the current task.
Set Suite Variable
Check Test Case ${TESTNAME} 1
diff --git a/atest/robot/standard_libraries/builtin/should_be_equal.robot b/atest/robot/standard_libraries/builtin/should_be_equal.robot
index 30f8f35dd31..9469e0caf25 100644
--- a/atest/robot/standard_libraries/builtin/should_be_equal.robot
+++ b/atest/robot/standard_libraries/builtin/should_be_equal.robot
@@ -5,11 +5,11 @@ Resource builtin_resource.robot
*** Test Cases ***
Basics
${tc}= Check test case ${TESTNAME}
- Verify argument type message ${tc.kws[0].msgs[0]}
- Verify argument type message ${tc.kws[1].msgs[0]}
- Verify argument type message ${tc.kws[2].msgs[0]} float int
- Verify argument type message ${tc.kws[3].msgs[0]} bytes bytes
- Verify argument type message ${tc.kws[4].msgs[0]}
+ Verify argument type message ${tc[0, 0]}
+ Verify argument type message ${tc[1, 0]}
+ Verify argument type message ${tc[2, 0]} float int
+ Verify argument type message ${tc[3, 0]} bytes bytes
+ Verify argument type message ${tc[4, 0]}
Case-insensitive
Check Test Case ${TESTNAME}
@@ -37,11 +37,11 @@ Fails without values
Multiline comparison uses diff
${tc} = Check test case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[1]} foo\nbar\ndar\n\n!=\n\nfoo\nbar\ngar\n\ndar
+ Check Log Message ${tc[0, 1]} foo\nbar\ndar\n\n!=\n\nfoo\nbar\ngar\n\ndar
Multiline comparison with custom message
${tc} = Check test case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[1]} foo\nbar\ndar\n\n!=\n\nfoo\nbar\ngar\n\ndar
+ Check Log Message ${tc[0, 1]} foo\nbar\ndar\n\n!=\n\nfoo\nbar\ngar\n\ndar
Multiline comparison requires both multiline
Check test case ${TESTNAME}
@@ -57,17 +57,17 @@ formatter=repr/ascii with non-ASCII characters
formatter=repr with multiline
${tc} = Check test case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[1]} foo\nbar\ndar\n\n!=\n\nfoo\nbar\ngar\n\ndar
+ Check Log Message ${tc[0, 1]} foo\nbar\ndar\n\n!=\n\nfoo\nbar\ngar\n\ndar
formatter=repr with multiline and different line endings
${tc} = Check test case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[1]} 1\n2\n3\n\n!=\n\n1\n2\n3
- Check Log Message ${tc.kws[1].msgs[1]} 1\n2\n3\n\n!=\n\n1\n2\n3
+ Check Log Message ${tc[0, 1]} 1\n2\n3\n\n!=\n\n1\n2\n3
+ Check Log Message ${tc[1, 1]} 1\n2\n3\n\n!=\n\n1\n2\n3
formatter=repr/ascii with multiline and non-ASCII characters
${tc} = Check test case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[1]} Å\nÄ\n\Ö\n\n!=\n\nÅ\nÄ\n\Ö
- Check Log Message ${tc.kws[1].msgs[1]} Å\nÄ\n\Ö\n\n!=\n\nÅ\nÄ\n\Ö
+ Check Log Message ${tc[0, 1]} Å\nÄ\n\Ö\n\n!=\n\nÅ\nÄ\n\Ö
+ Check Log Message ${tc[1, 1]} Å\nÄ\n\Ö\n\n!=\n\nÅ\nÄ\n\Ö
Invalid formatter
Check test case ${TESTNAME}
@@ -80,22 +80,22 @@ Dictionaries of different type with same items pass
Bytes containing non-ascii characters
${tc}= Check test case ${TESTNAME}
- Verify argument type message ${tc.kws[0].msgs[0]} bytes bytes
- Verify argument type message ${tc.kws[1].msgs[0]} bytes bytes
+ Verify argument type message ${tc[0, 0]} bytes bytes
+ Verify argument type message ${tc[1, 0]} bytes bytes
Unicode and bytes with non-ascii characters
${tc}= Check test case ${TESTNAME}
- Verify argument type message ${tc.kws[0].msgs[0]} bytes str
+ Verify argument type message ${tc[0, 0]} bytes str
Types info is added if string representations are same
${tc}= Check test case ${TESTNAME}
- Verify argument type message ${tc.kws[0].msgs[0]} str int
+ Verify argument type message ${tc[0, 0]} str int
Should Not Be Equal
${tc}= Check test case ${TESTNAME}
- Verify argument type message ${tc.kws[0].msgs[0]} str str
- Verify argument type message ${tc.kws[1].msgs[0]} str int
- Verify argument type message ${tc.kws[2].msgs[0]} str str
+ Verify argument type message ${tc[0, 0]} str str
+ Verify argument type message ${tc[1, 0]} str int
+ Verify argument type message ${tc[2, 0]} str str
Should Not Be Equal case-insensitive
Check Test Case ${TESTNAME}
@@ -117,6 +117,6 @@ Should Not Be Equal and collapse spaces
Should Not Be Equal with bytes containing non-ascii characters
${tc}= Check test case ${TESTNAME}
- Verify argument type message ${tc.kws[0].msgs[0]} bytes bytes
- Verify argument type message ${tc.kws[1].msgs[0]} bytes str
- Verify argument type message ${tc.kws[2].msgs[0]} bytes bytes
+ Verify argument type message ${tc[0, 0]} bytes bytes
+ Verify argument type message ${tc[1, 0]} bytes str
+ Verify argument type message ${tc[2, 0]} bytes bytes
diff --git a/atest/robot/standard_libraries/builtin/should_be_equal_as_xxx.robot b/atest/robot/standard_libraries/builtin/should_be_equal_as_xxx.robot
index 15d42822d4e..c571788cffc 100644
--- a/atest/robot/standard_libraries/builtin/should_be_equal_as_xxx.robot
+++ b/atest/robot/standard_libraries/builtin/should_be_equal_as_xxx.robot
@@ -5,35 +5,35 @@ Resource builtin_resource.robot
*** Test Cases ***
Should Be Equal As Integers
${tc}= Check test case ${TESTNAME}
- Verify argument type message ${tc.kws[0].msgs[0]}
+ Verify argument type message ${tc[0, 0]}
Should Be Equal As Integers with base
Check test case ${TESTNAME}
Should Not Be Equal As Integers
${tc}= Check test case ${TESTNAME}
- Verify argument type message ${tc.kws[0].msgs[0]}
+ Verify argument type message ${tc[0, 0]}
Should Not Be Equal As Integers with base
Check test case ${TESTNAME}
Should Be Equal As Numbers
${tc}= Check test case ${TESTNAME}
- Verify argument type message ${tc.kws[0].msgs[0]}
+ Verify argument type message ${tc[0, 0]}
Should Be Equal As Numbers with precision
Check test case ${TESTNAME}
Should Not Be Equal As Numbers
${tc}= Check test case ${TESTNAME}
- Verify argument type message ${tc.kws[0].msgs[0]}
+ Verify argument type message ${tc[0, 0]}
Should Not Be Equal As Numbers with precision
Check test case ${TESTNAME}
Should Be Equal As Strings
${tc}= Check test case ${TESTNAME}
- Verify argument type message ${tc.kws[0].msgs[0]} int
+ Verify argument type message ${tc[0, 0]} int
Should Be Equal As Strings does NFC normalization
Check test case ${TESTNAME}
@@ -70,7 +70,7 @@ Should Be Equal As Strings repr multiline
Should Not Be Equal As Strings
${tc}= Check test case ${TESTNAME}
- Verify argument type message ${tc.kws[0].msgs[0]} str float
+ Verify argument type message ${tc[0, 0]} str float
Should Not Be Equal As Strings case-insensitive
Check test case ${TESTNAME}
diff --git a/atest/robot/standard_libraries/builtin/should_be_equal_type_conversion.robot b/atest/robot/standard_libraries/builtin/should_be_equal_type_conversion.robot
new file mode 100644
index 00000000000..752d7ad340b
--- /dev/null
+++ b/atest/robot/standard_libraries/builtin/should_be_equal_type_conversion.robot
@@ -0,0 +1,37 @@
+*** Settings ***
+Suite Setup Run Tests ${EMPTY} standard_libraries/builtin/should_be_equal_type_conversion.robot
+Resource atest_resource.robot
+
+*** Test Cases ***
+Convert second argument using `type`
+ Check Test Case ${TESTNAME}
+
+Automatic `type`
+ Check Test Case ${TESTNAME}
+
+Automatic `type` doesn't handle nested types
+ Check Test Case ${TESTNAME}
+
+First argument must match `type`
+ Check Test Case ${TESTNAME}
+
+Conversion fails with `type`
+ Check Test Case ${TESTNAME}
+
+Invalid type with `type`
+ Check Test Case ${TESTNAME}
+
+Convert both arguments using `types`
+ Check Test Case ${TESTNAME}
+
+Conversion fails with `types`
+ Check Test Case ${TESTNAME}
+
+Invalid type with `types`
+ Check Test Case ${TESTNAME}
+
+Cannot use both `type` and `types`
+ Check Test Case ${TESTNAME}
+
+Automatic type doesn't work with `types`
+ Check Test Case ${TESTNAME}
diff --git a/atest/robot/standard_libraries/builtin/should_contain.robot b/atest/robot/standard_libraries/builtin/should_contain.robot
index 148c1dc50ba..2e90122a0d2 100644
--- a/atest/robot/standard_libraries/builtin/should_contain.robot
+++ b/atest/robot/standard_libraries/builtin/should_contain.robot
@@ -27,6 +27,12 @@ Should Contain and do not collapse spaces
Should Contain and collapse spaces
Check Test Case ${TESTNAME}
+Should Contain with bytes
+ Check Test Case ${TESTNAME}
+
+Should Contain with bytearray
+ Check Test Case ${TESTNAME}
+
Should Not Contain
Check test case ${TESTNAME}
diff --git a/atest/robot/standard_libraries/builtin/sleep.robot b/atest/robot/standard_libraries/builtin/sleep.robot
index 9ff1a8fad74..454392bdafa 100644
--- a/atest/robot/standard_libraries/builtin/sleep.robot
+++ b/atest/robot/standard_libraries/builtin/sleep.robot
@@ -5,19 +5,19 @@ Resource atest_resource.robot
*** Test Cases ***
Sleep
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[1].msgs[0]} Slept 1 second 111 milliseconds.
- Check Log Message ${tc.kws[3].msgs[0]} Slept 1 second 234 milliseconds.
- Check Log Message ${tc.kws[5].msgs[0]} Slept 1 second 112 milliseconds.
+ Check Log Message ${tc[1, 0]} Slept 1 second 111 milliseconds.
+ Check Log Message ${tc[3, 0]} Slept 1 second 234 milliseconds.
+ Check Log Message ${tc[5, 0]} Slept 1 second 112 milliseconds.
Sleep With Negative Time
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[1].msgs[0]} Slept 0 seconds.
- Check Log Message ${tc.kws[2].msgs[0]} Slept 0 seconds.
+ Check Log Message ${tc[1, 0]} Slept 0 seconds.
+ Check Log Message ${tc[2, 0]} Slept 0 seconds.
Sleep With Reason
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} Slept 42 milliseconds.
- Check Log Message ${tc.kws[0].msgs[1]} No good reason
+ Check Log Message ${tc[0, 0]} Slept 42 milliseconds.
+ Check Log Message ${tc[0, 1]} No good reason
Invalid Time Does Not Cause Uncatchable Error
Check Test Case ${TESTNAME}
diff --git a/atest/robot/standard_libraries/builtin/tags.robot b/atest/robot/standard_libraries/builtin/tags.robot
index 886fde4cf54..0a4bf8eca0e 100644
--- a/atest/robot/standard_libraries/builtin/tags.robot
+++ b/atest/robot/standard_libraries/builtin/tags.robot
@@ -1,61 +1,61 @@
*** Settings ***
-Suite Setup Run Tests ${EMPTY} standard_libraries/builtin/tags
-Resource atest_resource.robot
+Suite Setup Run Tests ${EMPTY} standard_libraries/builtin/tags
+Resource atest_resource.robot
*** Variables ***
-@{SUITE_TAGS} default force force-init set set-init
+@{SUITE_TAGS} default force force-init set set-init
*** Test Cases ***
Set And Remove Tags In Suite Level
- Should Have Only Suite Tags Set And Remove Tags In Suite Level
+ Should Have Only Suite Tags Set And Remove Tags In Suite Level
Set No Tags
- Should Have Only Suite Tags Set No Tags
+ Should Have Only Suite Tags Set No Tags
Set One Tag
- ${tc} = Tags Should Have Been Added Set One Tag one
- Check Log Message ${tc.kws[0].msgs[0]} Set tag 'one'.
+ ${tc} = Tags Should Have Been Added Set One Tag one
+ Check Log Message ${tc[0, 0]} Set tag 'one'.
Set Multiple Tags
- ${tc} = Tags Should Have Been Added Set Multiple Tags 1 2 3 HELLO Some spaces here
- Check Log Message ${tc.kws[0].msgs[0]} Set tags '1', '2' and '3'.
- Check Log Message ${tc.kws[1].msgs[0]} Set tags 'HELLO', '' and 'Some spaces here'.
+ ${tc} = Tags Should Have Been Added Set Multiple Tags 1 2 3 HELLO Some spaces here
+ Check Log Message ${tc[0, 0]} Set tags '1', '2' and '3'.
+ Check Log Message ${tc[1, 0]} Set tags 'HELLO', '' and 'Some spaces here'.
Tags Set In One Test Are Not Visible To Others
- Should Have Only Suite Tags Tags Set In One Test Are Not Visible To Others
+ Should Have Only Suite Tags Tags Set In One Test Are Not Visible To Others
Remove No Tags
- Should Have Only Suite Tags Remove No Tags
+ Should Have Only Suite Tags Remove No Tags
Remove One Tag
- ${tc} = Tags Should Have Been Removed Remove One Tag force
- Check Log Message ${tc.kws[0].msgs[0]} Removed tag 'force'.
+ ${tc} = Tags Should Have Been Removed Remove One Tag force
+ Check Log Message ${tc[0, 0]} Removed tag 'force'.
Remove Non-Existing Tag
- Should Have Only Suite Tags Remove Non-Existing Tag
+ Should Have Only Suite Tags Remove Non-Existing Tag
Remove Multiple Tags
- ${tc} = Tags Should Have Been Removed Remove Multiple Tags default set set-init
- Check Log Message ${tc.kws[0].msgs[0]} Removed tags 'default', 'SET' and 'non-existing'.
- Check Log Message ${tc.kws[1].msgs[0]} Removed tags '' and 'set-init'.
+ ${tc} = Tags Should Have Been Removed Remove Multiple Tags default set set-init
+ Check Log Message ${tc[0, 0]} Removed tags 'default', 'SET' and 'non-existing'.
+ Check Log Message ${tc[1, 0]} Removed tags '' and 'set-init'.
Remove Tags With Pattern
- Check Test Tags Remove Tags With Pattern
+ Check Test Tags Remove Tags With Pattern
Tags Removed In One Test Are Not Removed From Others
- Should Have Only Suite Tags Tags Removed In One Test Are Not Removed From Others
+ Should Have Only Suite Tags Tags Removed In One Test Are Not Removed From Others
Set And Remove Tags In A User Keyword
- Check Test Tags Set And Remove Tags In A User Keyword tc uk uk2
+ Check Test Tags Set And Remove Tags In A User Keyword tc uk uk2
Set Tags In Test Setup
- Check Test Tags Set Tags In Test Setup set-init setup tag
+ Check Test Tags Set Tags In Test Setup set-init setup tag
Set Tags In Test Teardown
- Check Test Tags Set Tags In Test Teardown set-init teardown
+ Check Test Tags Set Tags In Test Teardown set-init teardown
Using Set And Remove Tags In Suite Teardown Fails
- Should Be Equal ${SUITE.suites[1].message} Suite teardown failed:\n'Set Tags' cannot be used in suite teardown.
+ Should Be Equal ${SUITE.suites[1].message} Suite teardown failed:\n'Set Tags' cannot be used in suite teardown.
Modifying ${TEST TAGS} after setting them has no affect on tags test has
Check Test Tags ${TEST NAME} force-init set-init new
@@ -65,19 +65,19 @@ Modifying ${TEST TAGS} after removing them has no affect on tags test has
*** Keywords ***
Should Have Only Suite Tags
- [Arguments] ${testname}
- Check Test Tags ${testname} @{SUITE_TAGS}
+ [Arguments] ${testname}
+ Check Test Tags ${testname} @{SUITE_TAGS}
Tags Should Have Been Added
- [Arguments] ${testname} @{added}
- @{tags} = Create List @{SUITE_TAGS} @{added}
- Sort List ${tags}
- ${tc} = Check Test Tags ${testname} @{tags}
- RETURN ${tc}
+ [Arguments] ${testname} @{added}
+ @{tags} = Create List @{SUITE_TAGS} @{added}
+ Sort List ${tags}
+ ${tc} = Check Test Tags ${testname} @{tags}
+ RETURN ${tc}
Tags Should Have Been Removed
- [Arguments] ${testname} @{removed}
- @{tags} = Copy List ${SUITE_TAGS}
- Remove Values From List ${tags} @{removed}
- ${tc} = Check Test Tags ${testname} @{tags}
- RETURN ${tc}
+ [Arguments] ${testname} @{removed}
+ @{tags} = Copy List ${SUITE_TAGS}
+ Remove Values From List ${tags} @{removed}
+ ${tc} = Check Test Tags ${testname} @{tags}
+ RETURN ${tc}
diff --git a/atest/robot/standard_libraries/builtin/used_in_custom_libs_and_listeners.robot b/atest/robot/standard_libraries/builtin/used_in_custom_libs_and_listeners.robot
index 2da3f4a1019..0f7eea8c51f 100644
--- a/atest/robot/standard_libraries/builtin/used_in_custom_libs_and_listeners.robot
+++ b/atest/robot/standard_libraries/builtin/used_in_custom_libs_and_listeners.robot
@@ -1,38 +1,65 @@
*** Settings ***
-Documentation These tests mainly verify that using BuiltIn externally does not cause importing problems as in
-... https://github.com/robotframework/robotframework/issues/654.
-... There are separate tests for creating and registering Run Keyword variants.
-Suite Setup Run Tests --listener ${CURDIR}/listener_using_builtin.py standard_libraries/builtin/used_in_custom_libs_and_listeners.robot
+Suite Setup Run Tests
+... --listener ${CURDIR}/listener_using_builtin.py
+... standard_libraries/builtin/used_in_custom_libs_and_listeners.robot
Resource atest_resource.robot
*** Test Cases ***
Keywords Using BuiltIn
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} Log level changed from INFO to DEBUG. DEBUG
- Check Log Message ${tc.kws[0].msgs[1]} Hello, debug world! DEBUG
+ Check Log Message ${tc[0, 0]} Log level changed from NONE to DEBUG. DEBUG
+ Check Log Message ${tc[0, 1]} Hello, debug world! DEBUG
+ Length should be ${tc[0].messages} 2
Listener Using BuiltIn
Check Test Case ${TESTNAME}
Use 'Run Keyword' with non-Unicode values
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} 42
- Check Log Message ${tc.kws[0].kws[1].msgs[0]} \\xff
+ Check Log Message ${tc[0, 0, 0]} 42
+ Check Log Message ${tc[0, 1, 0]} \xff
Use BuiltIn keywords with timeouts
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]} Test timeout 1 day active. * seconds left. level=DEBUG pattern=True
- Check Log Message ${tc.kws[0].msgs[1]} Log level changed from INFO to DEBUG. DEBUG
- Check Log Message ${tc.kws[0].msgs[2]} Hello, debug world! DEBUG
- Check Log Message ${tc.kws[3].kws[0].msgs[0]} Test timeout 1 day active. * seconds left. level=DEBUG pattern=True
- Check Log Message ${tc.kws[3].kws[0].msgs[1]} 42
- Check Log Message ${tc.kws[3].kws[1].msgs[0]} Test timeout 1 day active. * seconds left. level=DEBUG pattern=True
- Check Log Message ${tc.kws[3].kws[1].msgs[1]} \\xff
+ Check Log Message ${tc[0, 0]} Log level changed from NONE to DEBUG. DEBUG
+ Check Log Message ${tc[0, 1]} Hello, debug world! DEBUG
+ Length should be ${tc[0].messages} 2
+ Check Log Message ${tc[3, 0]} Test timeout 1 day active. * seconds left. level=DEBUG pattern=True
+ Check Log Message ${tc[3, 1, 0]} Test timeout 1 day active. * seconds left. level=DEBUG pattern=True
+ Check Log Message ${tc[3, 1, 1]} 42
+ Check Log Message ${tc[3, 2, 0]} Test timeout 1 day active. * seconds left. level=DEBUG pattern=True
+ Check Log Message ${tc[3, 2, 1]} \xff
User keyword used via 'Run Keyword'
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].kws[0].msgs[0]} This is x-911-zzz
+ Check Log Message ${tc[0, 0]} Before
+ Check Log Message ${tc[0, 1, 0, 0]} This is x-911-zzz
+ Check Log Message ${tc[0, 2]} After
User keyword used via 'Run Keyword' with timeout and trace level
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].kws[0].msgs[1]} This is x-911-zzz
+ Check Log Message ${tc[0, 0]} Arguments: [ \ ] level=TRACE
+ Check Log Message ${tc[0, 1]} Test timeout 1 day active. * seconds left. level=DEBUG pattern=True
+ Check Log Message ${tc[0, 2]} Before
+ Check Log Message ${tc[0, 3, 0]} Arguments: [ \${x}='This is x' | \${y}=911 | \${z}='zzz' ] level=TRACE
+ Check Log Message ${tc[0, 3, 1, 0]} Arguments: [ 'This is x-911-zzz' ] level=TRACE
+ Check Log Message ${tc[0, 3, 1, 1]} Keyword timeout 1 hour active. * seconds left. level=DEBUG pattern=True
+ Check Log Message ${tc[0, 3, 1, 2]} This is x-911-zzz
+ Check Log Message ${tc[0, 3, 1, 3]} Return: None level=TRACE
+ Check Log Message ${tc[0, 3, 2]} Return: None level=TRACE
+ Check Log Message ${tc[0, 4]} After
+ Check Log Message ${tc[0, 5]} Return: None level=TRACE
+
+Recursive 'Run Keyword' usage
+ ${tc} = Check Test Case ${TESTNAME}
+ Check Log Message ${tc[0, 0, 0]} 1
+ Check Log Message ${tc[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0]} 10
+
+Recursive 'Run Keyword' usage with timeout
+ Check Test Case ${TESTNAME}
+
+Timeout when running keyword that logs huge message
+ Check Test Case ${TESTNAME}
+
+Timeout in parent keyword after running keyword
+ Check Test Case ${TESTNAME}
diff --git a/atest/robot/standard_libraries/builtin/wait_until_keyword_succeeds.robot b/atest/robot/standard_libraries/builtin/wait_until_keyword_succeeds.robot
index 840c3e50d91..da2e5d8b791 100644
--- a/atest/robot/standard_libraries/builtin/wait_until_keyword_succeeds.robot
+++ b/atest/robot/standard_libraries/builtin/wait_until_keyword_succeeds.robot
@@ -6,20 +6,20 @@ Resource atest_resource.robot
Fail Because Timeout exceeded
${tc} = Check Test Case ${TESTNAME}
# Cannot test exactly how many times kw is run because it depends on interpreter speed.
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} Still 2 times to fail! FAIL
- Should Be True len($tc.kws[0].kws) < 4
+ Check Log Message ${tc[0, 0, 0]} Still 2 times to fail! FAIL
+ Should Be True len($tc[0].non_messages) < 4
Pass with first Try
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} Used to test that variable name, not value, is shown in arguments
- Length Should Be ${tc.kws[0].kws} 1
+ Check Log Message ${tc[0, 0, 0]} Used to test that variable name, not value, is shown in arguments
+ Length Should Be ${tc[0].body} 1
Pass With Some Medium Try
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].msgs[0]} Still 2 times to fail! FAIL
- Check Log Message ${tc.kws[0].kws[1].msgs[0]} Still 1 times to fail! FAIL
- Check Log Message ${tc.kws[0].kws[2].msgs[0]} Still 0 times to fail! FAIL
- Length Should Be ${tc.kws[0].kws} 4
+ Check Log Message ${tc[0, 0, 0]} Still 2 times to fail! FAIL
+ Check Log Message ${tc[0, 1, 0]} Still 1 times to fail! FAIL
+ Check Log Message ${tc[0, 2, 0]} Still 0 times to fail! FAIL
+ Length Should Be ${tc[0].body} 4
Pass With Last Possible Try
Check Test Case ${TESTNAME}
@@ -83,37 +83,37 @@ Retry if wrong number of arguments
Retry if variable is not found
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].kws[0].msgs[0]} Variable '\${nonexisting}' not found. FAIL
- Check Log Message ${tc.kws[0].kws[1].kws[0].msgs[0]} Variable '\${nonexisting}' not found. FAIL
- Check Log Message ${tc.kws[0].kws[2].kws[0].msgs[0]} Variable '\${nonexisting}' not found. FAIL
- Length Should Be ${tc.kws[0].kws} 3
+ Check Log Message ${tc[0, 0, 0, 0]} Variable '\${nonexisting}' not found. FAIL
+ Check Log Message ${tc[0, 1, 0, 0]} Variable '\${nonexisting}' not found. FAIL
+ Check Log Message ${tc[0, 2, 0, 0]} Variable '\${nonexisting}' not found. FAIL
+ Length Should Be ${tc[0].non_messages} 3
Pass With Initially Nonexisting Variable Inside Wait Until Keyword Succeeds
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].kws[0].kws[0].msgs[0]} Variable '\${created after accessing first time}' not found. FAIL
- Check Log Message ${tc.kws[0].kws[1].kws[0].msgs[0]} created in keyword teardown
- Length Should Be ${tc.kws[0].kws} 2
+ Check Log Message ${tc[0, 0, 0, 0]} Variable '\${created after accessing first time}' not found. FAIL
+ Check Log Message ${tc[0, 1, 0, 0]} created in keyword teardown
+ Length Should Be ${tc[0].body} 2
Variable Values Should Not Be Visible In Keyword Arguments
${tc} = Check Test Case Pass With First Try
- Check Keyword Data ${tc.kws[0].kws[0]} BuiltIn.Log args=\${HELLO}
+ Check Keyword Data ${tc[0, 0]} BuiltIn.Log args=\${HELLO}
Strict retry interval
${tc} = Check Test Case ${TESTNAME}
- Length Should Be ${tc.body[0].kws} 4
- Elapsed Time Should Be Valid ${tc.body[0].elapsed_time} minimum=0.3 maximum=0.9
+ Length Should Be ${tc[0].body} 4
+ Elapsed Time Should Be Valid ${tc[0].elapsed_time} minimum=0.3 maximum=0.9
Fail with strict retry interval
${tc} = Check Test Case ${TESTNAME}
- Length Should Be ${tc.body[0].kws} 3
- Elapsed Time Should Be Valid ${tc.body[0].elapsed_time} minimum=0.2 maximum=0.6
+ Length Should Be ${tc[0].non_messages} 3
+ Elapsed Time Should Be Valid ${tc[0].elapsed_time} minimum=0.2 maximum=0.6
Strict retry interval violation
${tc} = Check Test Case ${TESTNAME}
- Length Should Be ${tc.body[0].kws} 4
- Elapsed Time Should Be Valid ${tc.body[0].elapsed_time} minimum=0.4 maximum=1.2
+ Length Should Be ${tc[0].non_messages} 4
+ Elapsed Time Should Be Valid ${tc[0].elapsed_time} minimum=0.4 maximum=1.2
FOR ${index} IN 1 3 5 7
- Check Log Message ${tc.body[0].body[${index}]}
+ Check Log Message ${tc[0, ${index}]}
... Keyword execution time ??? milliseconds is longer than retry interval 100 milliseconds.
... WARN pattern=True
END
diff --git a/atest/robot/standard_libraries/collections/dictionaries_should_be_equal.robot b/atest/robot/standard_libraries/collections/dictionaries_should_be_equal.robot
index 17b59691d55..bf677da6fb9 100644
--- a/atest/robot/standard_libraries/collections/dictionaries_should_be_equal.robot
+++ b/atest/robot/standard_libraries/collections/dictionaries_should_be_equal.robot
@@ -65,3 +65,9 @@ Different values and custom error message with values
`ignore_case` when normalized keys have conflict
Check Test Case ${TESTNAME}
+
+`ignore_value_order` set to True
+ Check Test Case ${TESTNAME}
+
+`ignore_value_order` set to False and dictionaries have lists in different order
+ Check Test Case ${TESTNAME}
diff --git a/atest/robot/standard_libraries/collections/dictionary.robot b/atest/robot/standard_libraries/collections/dictionary.robot
index a29ffc9646f..8a386a2a70b 100644
--- a/atest/robot/standard_libraries/collections/dictionary.robot
+++ b/atest/robot/standard_libraries/collections/dictionary.robot
@@ -18,9 +18,9 @@ Set To Dictionary With **kwargs
Remove From Dictionary
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} Removed item with key 'b' and value '2'.
- Check Log Message ${tc.kws[0].msgs[1]} Key 'x' not found.
- Check Log Message ${tc.kws[0].msgs[2]} Key '2' not found.
+ Check Log Message ${tc[0, 0]} Removed item with key 'b' and value '2'.
+ Check Log Message ${tc[0, 1]} Key 'x' not found.
+ Check Log Message ${tc[0, 2]} Key '2' not found.
Keep In Dictionary
Check Test Case ${TEST NAME}
@@ -72,17 +72,17 @@ Log Dictionary With Different Log Levels
... a: 1
... b: 2
... c:
- Check Log Message ${tc.kws[0].msgs[0]} ${expected} INFO
- Should Be Empty ${tc.kws[1].msgs}
- Check Log Message ${tc.kws[2].msgs[0]} ${expected} WARN
- Check Log Message ${tc.kws[3].msgs[0]} ${expected} DEBUG
- Check Log Message ${tc.kws[4].msgs[0]} ${expected} INFO
+ Check Log Message ${tc[0, 0]} ${expected} INFO
+ Should Be Empty ${tc[1].body}
+ Check Log Message ${tc[2, 0]} ${expected} WARN
+ Check Log Message ${tc[3, 0]} ${expected} DEBUG
+ Check Log Message ${tc[4, 0]} ${expected} INFO
Log Dictionary With Different Dictionaries
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} Dictionary is empty.
- Check Log Message ${tc.kws[1].msgs[0]} Dictionary has one item:\na: 1
- Check Log Message ${tc.kws[3].msgs[0]} Dictionary size is 3 and it contains following items:\nTrue: xxx\nfoo: []\n(1, 2, 3): 3.14
+ Check Log Message ${tc[0, 0]} Dictionary is empty.
+ Check Log Message ${tc[1, 0]} Dictionary has one item:\na: 1
+ Check Log Message ${tc[3, 0]} Dictionary size is 3 and it contains following items:\nTrue: xxx\nfoo: []\n(1, 2, 3): 3.14
Pop From Dictionary Without Default
Check Test Case ${TEST NAME}
diff --git a/atest/robot/standard_libraries/collections/dictionary_should_contain.robot b/atest/robot/standard_libraries/collections/dictionary_should_contain.robot
index 5534fa70333..c13ab3e559b 100644
--- a/atest/robot/standard_libraries/collections/dictionary_should_contain.robot
+++ b/atest/robot/standard_libraries/collections/dictionary_should_contain.robot
@@ -95,3 +95,9 @@ Should contain sub dictionary with `ignore_case`
`has_key` is not required
Check Test Case ${TESTNAME}
+
+Should contain sub dictionary with `ignore_value_order`
+ Check Test Case ${TESTNAME}
+
+Should contain sub dictionary with `ignore_value_order` set to False when dictionaries have lists in different order
+ Check Test Case ${TESTNAME}
diff --git a/atest/robot/standard_libraries/collections/list.robot b/atest/robot/standard_libraries/collections/list.robot
index 636f6c60e42..75573b13e78 100644
--- a/atest/robot/standard_libraries/collections/list.robot
+++ b/atest/robot/standard_libraries/collections/list.robot
@@ -60,8 +60,8 @@ Remove From List With Invalid Index
Remove Duplicates
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} 0 duplicates removed.
- Check Log Message ${tc.kws[2].msgs[0]} 3 duplicates removed.
+ Check Log Message ${tc[0, 0]} 0 duplicates removed.
+ Check Log Message ${tc[2, 0]} 3 duplicates removed.
Count Values In List
Check Test Case ${TEST NAME}
@@ -149,19 +149,19 @@ List Should Not Contain Duplicates Is Case And Space Sensitive
List Should Not Contain Duplicates With One Duplicate
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[1].msgs[0]} 'item' found 2 times.
+ Check Log Message ${tc[1, 0]} 'item' found 2 times.
List Should Not Contain Duplicates With Multiple Duplicates
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[1].msgs[0]} '2' found 2 times.
- Check Log Message ${tc.kws[1].msgs[1]} 'None' found 2 times.
- Check Log Message ${tc.kws[1].msgs[2]} '4' found 4 times.
- Check Log Message ${tc.kws[1].msgs[3]} '[1, 2, 3]' found 2 times.
- Check Log Message ${tc.kws[1].msgs[4]} '[]' found 10 times.
+ Check Log Message ${tc[1, 0]} '2' found 2 times.
+ Check Log Message ${tc[1, 1]} 'None' found 2 times.
+ Check Log Message ${tc[1, 2]} '4' found 4 times.
+ Check Log Message ${tc[1, 3]} '[1, 2, 3]' found 2 times.
+ Check Log Message ${tc[1, 4]} '[]' found 10 times.
List Should Not Contain Duplicates With Custom Error Message
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[2].msgs[0]} '42' found 42 times.
+ Check Log Message ${tc[2, 0]} '42' found 42 times.
Lists Should Be Equal
Check Test Case ${TEST NAME}
@@ -211,6 +211,9 @@ List Should Contain Sub List
List Should Contain Sub List With Missing Values
Check Test Case ${TEST NAME}
+List Should Contain Sub List When The Only Missing Value Is Empty String
+ Check Test Case ${TEST NAME}
+
List Should Contain Sub List With Missing Values And Own Error Message
Check Test Case ${TEST NAME}
@@ -224,18 +227,18 @@ Log List With Different Log Levels
... 0: 11
... 1: 12
... 2: 13
- Check Log Message ${tc.kws[0].msgs[0]} ${expected} INFO
- Variable Should Not Exist ${tc.kws[1].msgs[0]}
- Check Log Message ${tc.kws[2].msgs[0]} ${expected} WARN
- Check Log Message ${tc.kws[3].msgs[0]} ${expected} DEBUG
- Check Log Message ${tc.kws[4].msgs[0]} ${expected} INFO
+ Check Log Message ${tc[0, 0]} ${expected} INFO
+ Variable Should Not Exist ${tc[1, 0]}
+ Check Log Message ${tc[2, 0]} ${expected} WARN
+ Check Log Message ${tc[3, 0]} ${expected} DEBUG
+ Check Log Message ${tc[4, 0]} ${expected} INFO
Log List With Different Lists
${tc} = Check Test Case ${TEST NAME}
- Check Log Message ${tc.kws[0].msgs[0]} List is empty. INFO
- Check Log Message ${tc.kws[1].msgs[0]} List has one item:\n1
- Check Log Message ${tc.kws[4].msgs[0]} List has one item:\n(1, 2, 3)
- Check Log Message ${tc.kws[6].msgs[0]} List length is 2 and it contains following items:\n0: (1, 2, 3)\n1: 3.12
+ Check Log Message ${tc[0, 0]} List is empty. INFO
+ Check Log Message ${tc[1, 0]} List has one item:\n1
+ Check Log Message ${tc[4, 0]} List has one item:\n(1, 2, 3)
+ Check Log Message ${tc[6, 0]} List length is 2 and it contains following items:\n0: (1, 2, 3)\n1: 3.12
Count Matches In List Case Insensitive
Check Test Case ${TEST NAME}
@@ -350,3 +353,6 @@ List Should Not Contain Duplicates With Ignore Case
List Should Contain Value With Ignore Case And Nested List and Dictionary
Check Test Case ${TEST NAME}
+
+Lists Should be equal with Ignore Case and Order
+ Check Test Case ${TEST NAME}
diff --git a/atest/robot/standard_libraries/dialogs/dialogs.robot b/atest/robot/standard_libraries/dialogs/dialogs.robot
index 4ecc2e49f5c..bb049c007e6 100644
--- a/atest/robot/standard_libraries/dialogs/dialogs.robot
+++ b/atest/robot/standard_libraries/dialogs/dialogs.robot
@@ -46,6 +46,21 @@ Get Value From User Shortcuts
Get Selection From User
Check Test Case ${TESTNAME}
+Get Selection From User When Default Value Provided by Index
+ Check Test Case ${TESTNAME}
+
+Get Selection From User When Default Value Provided by String
+ Check Test Case ${TESTNAME}
+
+Get Selection From User When Default Value Is Integer
+ Check Test Case ${TESTNAME}
+
+Get Selection From User When Default Value Index Is Out of Bounds
+ Check Test Case ${TESTNAME}
+
+Get Selection From User When Default Value Cannot Be Found
+ Check Test Case ${TESTNAME}
+
Get Selection From User Cancelled
Check Test Case ${TESTNAME}
@@ -66,3 +81,9 @@ Get Selections From User Exited
Multiple dialogs in a row
Check Test Case ${TESTNAME}
+
+Garbage Collection In Thread Should Not Cause Problems
+ Check Test Case ${TESTNAME}
+
+Timeout can close dialog
+ Check Test Case ${TESTNAME}
diff --git a/atest/robot/standard_libraries/easter.robot b/atest/robot/standard_libraries/easter.robot
index 7876de7afb4..cf8097982c5 100644
--- a/atest/robot/standard_libraries/easter.robot
+++ b/atest/robot/standard_libraries/easter.robot
@@ -8,6 +8,6 @@ Not None Shall Not Pass
None Shall Pass
${tc} = Check Test Case ${TESTNAME}
- Check Log Message ${tc.kws[0].msgs[0]}
+ Check Log Message ${tc[0, 0]}
...
-
+a
@@ -37,7 +37,7 @@ Examples are only using features compatible with all tested versions.
-
+Some doc.
diff --git a/atest/testdata/libdoc/BackwardsCompatibility-5.0.json b/atest/testdata/libdoc/BackwardsCompatibility-5.0.json
index 7cf578d7c31..fcf7f2b6428 100644
--- a/atest/testdata/libdoc/BackwardsCompatibility-5.0.json
+++ b/atest/testdata/libdoc/BackwardsCompatibility-5.0.json
@@ -76,7 +76,7 @@
"shortdoc": "",
"tags": [],
"source": "BackwardsCompatibility.py",
- "lineno": 42
+ "lineno": 39
},
{
"name": "Simple",
@@ -87,7 +87,7 @@
"example"
],
"source": "BackwardsCompatibility.py",
- "lineno": 34
+ "lineno": 31
},
{
"name": "Special Types",
diff --git a/atest/testdata/libdoc/BackwardsCompatibility-5.0.xml b/atest/testdata/libdoc/BackwardsCompatibility-5.0.xml
index 23675373534..3322b36d4da 100644
--- a/atest/testdata/libdoc/BackwardsCompatibility-5.0.xml
+++ b/atest/testdata/libdoc/BackwardsCompatibility-5.0.xml
@@ -11,7 +11,7 @@ Examples are only using features compatible with all tested versions.
-
+a
@@ -37,7 +37,7 @@ Examples are only using features compatible with all tested versions.
-
+Some doc.
diff --git a/atest/testdata/libdoc/BackwardsCompatibility-6.1.json b/atest/testdata/libdoc/BackwardsCompatibility-6.1.json
index b5dde92e6fb..e2a6ef6a981 100644
--- a/atest/testdata/libdoc/BackwardsCompatibility-6.1.json
+++ b/atest/testdata/libdoc/BackwardsCompatibility-6.1.json
@@ -82,7 +82,7 @@
"shortdoc": "",
"tags": [],
"source": "BackwardsCompatibility.py",
- "lineno": 42
+ "lineno": 39
},
{
"name": "Simple",
@@ -93,7 +93,7 @@
"example"
],
"source": "BackwardsCompatibility.py",
- "lineno": 34
+ "lineno": 31
},
{
"name": "Special Types",
diff --git a/atest/testdata/libdoc/BackwardsCompatibility-6.1.xml b/atest/testdata/libdoc/BackwardsCompatibility-6.1.xml
index 8fe3a21ba8d..c721cb2b2c8 100644
--- a/atest/testdata/libdoc/BackwardsCompatibility-6.1.xml
+++ b/atest/testdata/libdoc/BackwardsCompatibility-6.1.xml
@@ -11,7 +11,7 @@ Examples are only using features compatible with all tested versions.
-
+a
@@ -37,7 +37,7 @@ Examples are only using features compatible with all tested versions.
-
+Some doc.
diff --git a/atest/testdata/libdoc/BackwardsCompatibility.py b/atest/testdata/libdoc/BackwardsCompatibility.py
index caf49841afe..b8403a3eedf 100644
--- a/atest/testdata/libdoc/BackwardsCompatibility.py
+++ b/atest/testdata/libdoc/BackwardsCompatibility.py
@@ -5,28 +5,25 @@
"""
from enum import Enum
-from typing import Union
-try:
- from typing_extensions import TypedDict
-except ImportError:
- from typing import TypedDict
+from typing import TypedDict, Union
+ROBOT_LIBRARY_VERSION = "1.0"
-ROBOT_LIBRARY_VERSION = '1.0'
-
-__all__ = ['simple', 'arguments', 'types', 'special_types', 'union']
+__all__ = ["simple", "arguments", "types", "special_types", "union"]
class Color(Enum):
"""RGB colors."""
- RED = 'R'
- GREEN = 'G'
- BLUE = 'B'
+
+ RED = "R"
+ GREEN = "G"
+ BLUE = "B"
class Size(TypedDict):
"""Some size."""
+
width: int
height: int
diff --git a/atest/testdata/libdoc/DataTypesLibrary.py b/atest/testdata/libdoc/DataTypesLibrary.py
index 4b57df394b5..96a980e688c 100644
--- a/atest/testdata/libdoc/DataTypesLibrary.py
+++ b/atest/testdata/libdoc/DataTypesLibrary.py
@@ -1,9 +1,9 @@
+import sys
from enum import Enum, IntEnum
-from typing import Any, Dict, List, Literal, Optional, Union
-try:
+from typing import Any, Dict, List, Literal, Optional, TypedDict, Union
+
+if sys.version_info < (3, 9):
from typing_extensions import TypedDict
-except ImportError:
- from typing import TypedDict
from robot.api.deco import library
@@ -27,6 +27,7 @@ class GeoLocation(_GeoCoordinated, total=False):
Example usage: ``{'latitude': 59.95, 'longitude': 30.31667}``
"""
+
accuracy: float
@@ -35,6 +36,7 @@ class Small(IntEnum):
This was defined within the class definition.
"""
+
one = 1
two = 2
three = 3
@@ -49,7 +51,7 @@ class Small(IntEnum):
"<": "<",
">": ">",
"<=": "<=",
- ">=": ">="
+ ">=": ">=",
},
)
AssertionOperator.__doc__ = """This is some Doc
@@ -59,6 +61,7 @@ class Small(IntEnum):
class CustomType:
"""This doc not used because converter method has doc."""
+
@classmethod
def parse(cls, value: Union[str, int]):
"""Converter method doc is used when defined."""
@@ -67,6 +70,7 @@ def parse(cls, value: Union[str, int]):
class CustomType2:
"""Class doc is used when converter method has no doc."""
+
def __init__(self, value):
self.value = value
@@ -81,10 +85,14 @@ def not_used_converter_should_not_be_documented(cls, value):
return 1
-@library(converters={CustomType: CustomType.parse,
- CustomType2: CustomType2,
- A: A.not_used_converter_should_not_be_documented},
- auto_keywords=True)
+@library(
+ converters={
+ CustomType: CustomType.parse,
+ CustomType2: CustomType2,
+ A: A.not_used_converter_should_not_be_documented,
+ },
+ auto_keywords=True,
+)
class DataTypesLibrary:
"""This Library has Data Types.
@@ -104,32 +112,44 @@ def __init__(self, credentials: Small = Small.one):
def set_location(self, location: GeoLocation) -> bool:
return True
- def assert_something(self, value, operator: Optional[AssertionOperator] = None, exp: str = 'something?'):
+ def assert_something(
+ self,
+ value,
+ operator: Optional[AssertionOperator] = None,
+ exp: str = "something?",
+ ):
"""This links to `AssertionOperator` .
This is the next Line that links to `Set Location` .
"""
pass
- def funny_unions(self,
- funny: Union[
- bool,
- Union[
- int,
- float,
- bool,
- str,
- AssertionOperator,
- Small,
- GeoLocation,
- None]] = AssertionOperator.equal) -> Union[int, List[int]]:
+ def funny_unions(
+ self,
+ funny: Union[
+ bool,
+ Union[int, float, bool, str, AssertionOperator, Small, GeoLocation, None],
+ ] = AssertionOperator.equal,
+ ) -> Union[int, List[int]]:
pass
- def typing_types(self, list_of_str: List[str], dict_str_int: Dict[str, int], whatever: Any, *args: List[Any]):
+ def typing_types(
+ self,
+ list_of_str: List[str],
+ dict_str_int: Dict[str, int],
+ whatever: Any,
+ *args: List[Any],
+ ):
pass
- def x_literal(self, arg: Literal[1, 'xxx', b'yyy', True, None, Small.one]):
+ def x_literal(self, arg: Literal[1, "xxx", b"yyy", True, None, Small.one]):
pass
- def custom(self, arg: CustomType, arg2: 'CustomType2', arg3: CustomType, arg4: Unknown):
+ def custom(
+ self,
+ arg: CustomType,
+ arg2: "CustomType2",
+ arg3: CustomType,
+ arg4: Unknown,
+ ):
pass
diff --git a/atest/testdata/libdoc/Decorators.py b/atest/testdata/libdoc/Decorators.py
index 60fb4c7bf9e..169901cb85c 100644
--- a/atest/testdata/libdoc/Decorators.py
+++ b/atest/testdata/libdoc/Decorators.py
@@ -1,12 +1,12 @@
from functools import wraps
-
-__all__ = ['keyword_using_decorator', 'keyword_using_decorator_with_wraps']
+__all__ = ["keyword_using_decorator", "keyword_using_decorator_with_wraps"]
def decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
+
return wrapper
@@ -14,12 +14,13 @@ def decorator_with_wraps(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
+
return wrapper
@decorator
def keyword_using_decorator(args, are_not, preserved=True):
- return '%s %s %s' % (args, are_not, preserved)
+ return f"{args} {are_not} {preserved}"
@decorator_with_wraps
diff --git a/atest/testdata/libdoc/DocFormatHtml.py b/atest/testdata/libdoc/DocFormatHtml.py
index 8e8b9ec5b07..6efd4c82c7d 100644
--- a/atest/testdata/libdoc/DocFormatHtml.py
+++ b/atest/testdata/libdoc/DocFormatHtml.py
@@ -2,7 +2,7 @@
class DocFormatHtml(DocFormat):
- ROBOT_LIBRARY_DOC_FORMAT = 'HtMl'
+ ROBOT_LIBRARY_DOC_FORMAT = "HtMl"
DocFormatHtml.__doc__ = DocFormat.__doc__
diff --git a/atest/testdata/libdoc/DocFormatInvalid.py b/atest/testdata/libdoc/DocFormatInvalid.py
index ee0112cc027..54bd548d2e4 100644
--- a/atest/testdata/libdoc/DocFormatInvalid.py
+++ b/atest/testdata/libdoc/DocFormatInvalid.py
@@ -2,7 +2,7 @@
class DocFormatInvalid(DocFormat):
- ROBOT_LIBRARY_DOC_FORMAT = 'invalid'
+ ROBOT_LIBRARY_DOC_FORMAT = "invalid"
DocFormatInvalid.__doc__ = DocFormat.__doc__
diff --git a/atest/testdata/libdoc/DocSetInInit.py b/atest/testdata/libdoc/DocSetInInit.py
index 0f0be26f07d..c3498cc4c6c 100644
--- a/atest/testdata/libdoc/DocSetInInit.py
+++ b/atest/testdata/libdoc/DocSetInInit.py
@@ -1,4 +1,4 @@
class DocSetInInit:
def __init__(self):
- self.__doc__ = 'Doc set in __init__!!'
+ self.__doc__ = "Doc set in __init__!!"
diff --git a/atest/testdata/libdoc/DynamicLibrary.py b/atest/testdata/libdoc/DynamicLibrary.py
index 1c4d45dba5b..19508b0969d 100644
--- a/atest/testdata/libdoc/DynamicLibrary.py
+++ b/atest/testdata/libdoc/DynamicLibrary.py
@@ -4,57 +4,63 @@
class DynamicLibrary:
"""This doc is overwritten and not shown in docs."""
+
ROBOT_LIBRARY_VERSION = 0.1
def __init__(self, arg1, arg2="These args are shown in docs"):
"""This doc is overwritten and not shown in docs."""
def get_keyword_names(self):
- return ['0',
- 'Keyword 1',
- 'KW2',
- 'no arg spec',
- 'Defaults',
- 'Keyword-only args',
- 'KWO w/ varargs',
- 'Embedded ${args} 1',
- 'Em${bed}ed ${args} 2',
- 'nön-äscii ÜTF-8'.encode('UTF-8'),
- 'nön-äscii Ünicöde',
- 'Tags',
- 'Types',
- 'Source info',
- 'Source path only',
- 'Source lineno only',
- 'Non-existing source path and lineno',
- 'Non-existing source path with lineno',
- 'Invalid source info']
+ return [
+ "0",
+ "Keyword 1",
+ "KW2",
+ "no arg spec",
+ "Defaults",
+ "Keyword-only args",
+ "KWO w/ varargs",
+ "Embedded ${args} 1",
+ "Em${bed}ed ${args} 2",
+ "nön-äscii ÜTF-8".encode("UTF-8"),
+ "nön-äscii Ünicöde",
+ "Tags",
+ "Types",
+ "Source info",
+ "Source path only",
+ "Source lineno only",
+ "Non-existing source path and lineno",
+ "Non-existing source path with lineno",
+ "Invalid source info",
+ ]
def run_keyword(self, name, args, kwargs):
print(name, args)
def get_keyword_arguments(self, name):
- if name == 'Defaults':
- return ['old=style', ('new', 'style'), ('cool', True)]
- if name == 'Keyword-only args':
- return ['*', 'kwo', 'another=default']
- if name == 'KWO w/ varargs':
- return ['*varargs', 'a', ('b', 2), 'c', '**kws']
- if name == 'Types':
- return ['integer', 'no type', ('boolean', True)]
+ if name == "Defaults":
+ return ["old=style", ("new", "style"), ("cool", True)]
+ if name == "Keyword-only args":
+ return ["*", "kwo", "another=default"]
+ if name == "KWO w/ varargs":
+ return ["*varargs", "a", ("b", 2), "c", "**kws"]
+ if name == "Types":
+ return ["integer", "no type", ("boolean", True)]
if not name[-1].isdigit():
return None
- return ['arg%d' % (i+1) for i in range(int(name[-1]))]
+ return [f"arg{i + 1}" for i in range(int(name[-1]))]
def get_keyword_documentation(self, name):
- if name == 'nön-äscii ÜTF-8':
- return 'Hyvää yötä.\n\nСпасибо! (UTF-8)\n\nTags: hyvää, yötä'.encode('UTF-8')
- if name == 'nön-äscii Ünicöde':
- return 'Hyvää yötä.\n\nСпасибо! (Unicode)\n\nTags: hyvää, yötä'
- short = 'Dummy documentation for `%s`.' % name
- if name.startswith('__'):
+ non_ascii = "Hyvää yötä.\n\nСпасибо! ({})\n\nTags: hyvää, yötä"
+ if name == "nön-äscii Ünicöde":
+ return non_ascii.format("Unicode")
+ if name == "nön-äscii ÜTF-8":
+ return non_ascii.format("UTF-8").encode("UTF-8")
+ short = f"Dummy documentation for `{name}`."
+ if name.startswith("__"):
return short
- return short + '''
+ return (
+ short
+ + """
Neither `Keyword 1` or `KW 2` do anything really interesting.
They do, however, accept some `arguments`.
@@ -68,31 +74,32 @@ def get_keyword_documentation(self, name):
-------
http://robotframework.org
-'''
+"""
+ )
def get_keyword_tags(self, name):
- if name == 'Tags':
- return ['my', 'tägs']
+ if name == "Tags":
+ return ["my", "tägs"]
return None
def get_keyword_types(self, name):
- if name == 'Types':
- return {'integer': int, 'boolean': bool, 'return': int}
+ if name == "Types":
+ return {"integer": int, "boolean": bool, "return": int}
return None
def get_keyword_source(self, name):
- if name == 'Source info':
+ if name == "Source info":
path = inspect.getsourcefile(type(self))
lineno = inspect.getsourcelines(self.get_keyword_source)[1]
- return '%s:%s' % (path, lineno)
- if name == 'Source path only':
- return os.path.dirname(__file__) + '/Annotations.py'
- if name == 'Source lineno only':
- return ':12345'
- if name == 'Non-existing source path and lineno':
- return 'whatever:xxx'
- if name == 'Non-existing source path with lineno':
- return 'everwhat:42'
- if name == 'Invalid source info':
+ return f"{path}:{lineno}"
+ if name == "Source path only":
+ return os.path.dirname(__file__) + "/Annotations.py"
+ if name == "Source lineno only":
+ return ":12345"
+ if name == "Non-existing source path and lineno":
+ return "whatever:xxx"
+ if name == "Non-existing source path with lineno":
+ return "everwhat:42"
+ if name == "Invalid source info":
return 123
return None
diff --git a/atest/testdata/libdoc/DynamicLibraryWithoutGetKwArgsAndDoc.py b/atest/testdata/libdoc/DynamicLibraryWithoutGetKwArgsAndDoc.py
index 501b033c628..b7b8b731f25 100644
--- a/atest/testdata/libdoc/DynamicLibraryWithoutGetKwArgsAndDoc.py
+++ b/atest/testdata/libdoc/DynamicLibraryWithoutGetKwArgsAndDoc.py
@@ -7,7 +7,7 @@ def __init__(self, doc=None):
self.__doc__ = doc
def get_keyword_names(self):
- return ['Keyword']
+ return ["Keyword"]
def run_keyword(self, name, args):
pass
diff --git a/atest/testdata/libdoc/InternalLinking.py b/atest/testdata/libdoc/InternalLinking.py
index 6479eb23309..d195c13a40b 100644
--- a/atest/testdata/libdoc/InternalLinking.py
+++ b/atest/testdata/libdoc/InternalLinking.py
@@ -1,5 +1,5 @@
class InternalLinking:
- u"""Library for testing libdoc's internal linking.
+ """Library for testing libdoc's internal linking.
= Linking to sections =
@@ -61,7 +61,7 @@ def second_keyword(self, arg):
"""
def escaping(self):
- u"""Escaped links:
+ """Escaped links:
- `Percent encoding: !"#%/()=?|+-_.!~*'()`
- `HTML entities: &<>`
- `Non-ASCII: \xe4\u2603`
diff --git a/atest/testdata/libdoc/InvalidKeywords.py b/atest/testdata/libdoc/InvalidKeywords.py
index 6556e80e7ea..5dbf9c3a91a 100644
--- a/atest/testdata/libdoc/InvalidKeywords.py
+++ b/atest/testdata/libdoc/InvalidKeywords.py
@@ -3,7 +3,7 @@
class InvalidKeywords:
- @keyword('Invalid embedded ${args}')
+ @keyword("Invalid embedded ${args}")
def invalid_embedded(self):
pass
@@ -13,11 +13,11 @@ def duplicate_name(self):
def duplicateName(self):
pass
- @keyword('Same ${embedded}')
+ @keyword("Same ${embedded}")
def dupe_with_embedded_1(self, arg):
pass
- @keyword('same ${match}')
+ @keyword("same ${match}")
def dupe_with_embedded_2(self, arg):
"""This is an error only at run time."""
pass
diff --git a/atest/testdata/libdoc/KeywordOnlyArgs.py b/atest/testdata/libdoc/KeywordOnlyArgs.py
deleted file mode 100644
index 9aef163fece..00000000000
--- a/atest/testdata/libdoc/KeywordOnlyArgs.py
+++ /dev/null
@@ -1,6 +0,0 @@
-def kw_only_args(*, kwo):
- pass
-
-
-def kw_only_args_with_varargs(*varargs, kwo, another='default'):
- pass
diff --git a/atest/testdata/libdoc/KwArgs.py b/atest/testdata/libdoc/KwArgs.py
new file mode 100644
index 00000000000..26667c3cd87
--- /dev/null
+++ b/atest/testdata/libdoc/KwArgs.py
@@ -0,0 +1,14 @@
+def kw_only_args(*, kwo):
+ pass
+
+
+def kw_only_args_with_varargs(*varargs, kwo, another="default"):
+ pass
+
+
+def kwargs_and_varargs(*varargs, **kwargs):
+ pass
+
+
+def kwargs_with_everything(a, /, b, c="d", *e, f, g="h", **i):
+ pass
diff --git a/atest/testdata/libdoc/LibraryArguments.py b/atest/testdata/libdoc/LibraryArguments.py
index ad16a7e14bd..baae66fcf55 100644
--- a/atest/testdata/libdoc/LibraryArguments.py
+++ b/atest/testdata/libdoc/LibraryArguments.py
@@ -1,7 +1,7 @@
class LibraryArguments:
def __init__(self, required, args: bool, optional=None):
- assert required == 'required'
+ assert required == "required"
assert args is True
def keyword(self):
diff --git a/atest/testdata/libdoc/LibraryDecorator.py b/atest/testdata/libdoc/LibraryDecorator.py
index c5c62fbe238..1c6bc6174d8 100644
--- a/atest/testdata/libdoc/LibraryDecorator.py
+++ b/atest/testdata/libdoc/LibraryDecorator.py
@@ -1,9 +1,9 @@
from robot.api.deco import keyword, library
-@library(version='3.2b1', scope='GLOBAL', doc_format='HTML')
+@library(version="3.2b1", scope="GLOBAL", doc_format="HTML")
class LibraryDecorator:
- ROBOT_LIBRARY_VERSION = 'overridden'
+ ROBOT_LIBRARY_VERSION = "overridden"
@keyword
def kw(self):
diff --git a/atest/testdata/libdoc/ReturnType.py b/atest/testdata/libdoc/ReturnType.py
index 48e4ed44524..22b5a25e180 100644
--- a/atest/testdata/libdoc/ReturnType.py
+++ b/atest/testdata/libdoc/ReturnType.py
@@ -21,7 +21,7 @@ def E_union_return() -> Union[int, float]:
return 42
-def F_stringified_return() -> 'int | float':
+def F_stringified_return() -> "int | float":
return 42
@@ -33,5 +33,5 @@ def G_unknown_return() -> Unknown:
return Unknown()
-def H_invalid_return() -> 'list[int':
+def H_invalid_return() -> "list[int": # noqa: F722
pass
diff --git a/atest/testdata/libdoc/TypesViaKeywordDeco.py b/atest/testdata/libdoc/TypesViaKeywordDeco.py
index 839ebbde393..605f106d9e3 100644
--- a/atest/testdata/libdoc/TypesViaKeywordDeco.py
+++ b/atest/testdata/libdoc/TypesViaKeywordDeco.py
@@ -5,42 +5,46 @@ class UnknownType:
pass
-@keyword(types={'integer': int, 'boolean': bool, 'string': str})
+@keyword(types={"integer": int, "boolean": bool, "string": str})
def A_basics(integer, boolean, string: int):
pass
-@keyword(types={'integer': int, 'list_': list})
+@keyword(types={"integer": int, "list_": list})
def B_with_defaults(integer=42, list_=None):
pass
-@keyword(types={'varargs': int, 'kwargs': bool})
+@keyword(types={"varargs": int, "kwargs": bool})
def C_varags_and_kwargs(*varargs, **kwargs):
pass
-@keyword(types={'unknown': UnknownType, 'unrecognized': Ellipsis})
+@keyword(types={"unknown": UnknownType, "unrecognized": Ellipsis})
def D_unknown_types(unknown, unrecognized):
pass
-@keyword(types={'arg': 'One of the usages in PEP-3107',
- 'varargs': 'But surely feels odd...'})
+@keyword(
+ types={
+ "arg": "One of the usages in PEP-3107",
+ "varargs": "But surely feels odd...",
+ }
+)
def E_non_type_annotations(arg, *varargs):
pass
-@keyword(types={'kwo': int, 'with_default': str})
-def F_kw_only_args(*, kwo, with_default='value'):
+@keyword(types={"kwo": int, "with_default": str})
+def F_kw_only_args(*, kwo, with_default="value"):
pass
-@keyword(types={'return': int})
+@keyword(types={"return": int})
def G_return_type() -> bool:
pass
-@keyword(types={'arg': int, 'return': (int, float)})
+@keyword(types={"arg": int, "return": (int, float)})
def G_return_type_as_tuple(arg):
pass
diff --git a/atest/testdata/libdoc/default_escaping.py b/atest/testdata/libdoc/default_escaping.py
index 306429674f8..0ab039af05d 100644
--- a/atest/testdata/libdoc/default_escaping.py
+++ b/atest/testdata/libdoc/default_escaping.py
@@ -1,35 +1,54 @@
"""Library to document and test correct default value escaping."""
+
from robot.libraries.BuiltIn import BuiltIn
b = BuiltIn()
-def verify_backslash(current='c:\\windows\\system', expected='c:\\windows\\system'):
+def verify_backslash(
+ current="c:\\windows\\system",
+ expected="c:\\windows\\system",
+):
b.should_be_equal(current, expected)
-def verify_internalvariables(current='first ${sca${lar}} @{list}[${4}] &{dict.key}[2] some env %{${somename}} and a \\${backslash}[${key}] ',
- expected='first ${sca${lar}} @{list}[${4}] &{dict.key}[2] some env %{${somename}} and a \\${backslash}[${key}] '):
+def verify_internalvariables(
+ current="first ${sca${lar}} @{list}[${4}] &{dict.key}[2] some env %{${somename}} and a \\${backslash}[${key}] ",
+ expected="first ${sca${lar}} @{list}[${4}] &{dict.key}[2] some env %{${somename}} and a \\${backslash}[${key}] ",
+):
b.should_be_equal(current, expected)
-def verify_line_break(current='Hello\n World!\r\n End...\\n', expected='Hello\n World!\r\n End...\\n'):
+def verify_line_break(
+ current="Hello\n World!\r\n End...\\n",
+ expected="Hello\n World!\r\n End...\\n",
+):
b.should_be_equal(current, expected)
-def verify_line_tab(current='Hello\tWorld!\t\t End\\t...', expected='Hello\tWorld!\t\t End\\t...'):
+def verify_line_tab(
+ current="Hello\tWorld!\t\t End\\t...",
+ expected="Hello\tWorld!\t\t End\\t...",
+):
b.should_be_equal(current, expected)
-def verify_spaces(current=' Hello\tW orld!\t \t En d\\t... ', expected=' Hello\tW orld!\t \t En d\\t... '):
+def verify_spaces(
+ current=" Hello\tW orld!\t \t En d\\t... ",
+ expected=" Hello\tW orld!\t \t En d\\t... ",
+):
b.should_be_equal(current, expected)
-def verify_variables(current='first ${scalar} then @{list} and &{dict.key}[2] some env %{username} and a \\${backslash} ',
- expected='first ${scalar} then @{list} and &{dict.key}[2] some env %{username} and a \\${backslash} '):
+def verify_variables(
+ current="first ${scalar} then @{list} and &{dict.key}[2] some env %{username} and a \\${backslash} ",
+ expected="first ${scalar} then @{list} and &{dict.key}[2] some env %{username} and a \\${backslash} ",
+):
b.should_be_equal(current, expected)
-def verify_all(current='first ${scalar} \nthen\t @{list} and \\\\&{dict.key}[2] so \\ me env %{username} and a \\${backslash} ',
- expected='first ${scalar} \nthen\t @{list} and \\\\&{dict.key}[2] so \\ me env %{username} and a \\${backslash} '):
+def verify_all(
+ current="first ${scalar} \nthen\t @{list} and \\\\&{dict.key}[2] so \\ me env %{username} and a \\${backslash} ",
+ expected="first ${scalar} \nthen\t @{list} and \\\\&{dict.key}[2] so \\ me env %{username} and a \\${backslash} ",
+):
b.should_be_equal(current, expected)
diff --git a/atest/testdata/libdoc/module.py b/atest/testdata/libdoc/module.py
index dec5c97fc20..7361f9d5dc4 100644
--- a/atest/testdata/libdoc/module.py
+++ b/atest/testdata/libdoc/module.py
@@ -2,11 +2,10 @@
from robot.api import deco
+__version__ = "0.1-alpha"
-__version__ = '0.1-alpha'
-
-def keyword(a1='d', *a2):
+def keyword(a1="d", *a2):
"""A keyword.
See `get hello` for details.
@@ -20,18 +19,18 @@ def get_hello():
See `importing` for explanation of nothing
and `introduction` for no more information.
"""
- return 'foo'
+ return "foo"
def non_string_defaults(a=1, b=True, c=(1, 2, None)):
pass
-def non_ascii_string_defaults(arg='hyvä'):
+def non_ascii_string_defaults(arg="hyvä"):
pass
-def non_ascii_bytes_defaults(arg=b'hyv\xe4'):
+def non_ascii_bytes_defaults(arg=b"hyv\xe4"):
pass
@@ -56,10 +55,10 @@ def non_ascii_doc():
def non_ascii_doc_with_escapes():
- """Hyv\xE4\xE4 y\xF6t\xE4."""
+ """Hyv\xe4\xe4 y\xf6t\xe4."""
-@deco.keyword('Set Name Using Robot Name Attribute')
+@deco.keyword("Set Name Using Robot Name Attribute")
def name_set_in_method_signature(a, b, *args, **kwargs):
"""
This makes sure that @deco.keyword decorated kws don't lose their signatures
@@ -67,30 +66,30 @@ def name_set_in_method_signature(a, b, *args, **kwargs):
pass
-@deco.keyword('Takes ${embedded} ${args}')
+@deco.keyword("Takes ${embedded} ${args}")
def takes_embedded_args(a=1, b=2):
"""A keyword which uses embedded args."""
pass
-@deco.keyword('Takes ${embedded} and normal args')
+@deco.keyword("Takes ${embedded} and normal args")
def takes_embedded_and_normal(embedded, mandatory, optional=None):
"""A keyword which uses embedded and normal args."""
pass
-@deco.keyword('Takes ${embedded} and positional-only args')
+@deco.keyword("Takes ${embedded} and positional-only args")
def takes_embedded_and_pos_only(embedded, mandatory, /, optional=None):
"""A keyword which uses embedded, positional-only and normal args."""
pass
-@deco.keyword(tags=['1', 1, 'one', 'yksi'])
+@deco.keyword(tags=["1", 1, "one", "yksi"])
def keyword_with_tags_1():
pass
-@deco.keyword('Keyword with tags 2', ('2', 2, 'two', 'kaksi'))
+@deco.keyword("Keyword with tags 2", ("2", 2, "two", "kaksi"))
def setting_both_name_and_tags_by_decorator():
pass
@@ -101,5 +100,6 @@ def keyword_with_tags_3():
Tags: tag1, tag2
"""
+
def robot_espacers(arg=" robot escapers\n\t\r "):
pass
diff --git a/atest/testdata/misc/everything.robot b/atest/testdata/misc/everything.robot
new file mode 100644
index 00000000000..c53799a6503
--- /dev/null
+++ b/atest/testdata/misc/everything.robot
@@ -0,0 +1,114 @@
+*** Settings ***
+Documentation This suite tries to cover all possible syntax.
+...
+... It can be used for testing different output files etc.
+... Features themselves are tested more thoroughly elsewhere.
+Metadata Name Value
+Suite Setup Log Library keyword
+Suite Teardown User Keyword
+Resource failing_import_creates_error.resource
+
+*** Test Cases ***
+Library keyword
+ Log Library keyword
+
+User keyword and RETURN
+ ${value} = User Keyword value
+ Should Be Equal ${value} return value
+
+Test documentation, tags and timeout
+ [Documentation] Hello, world!
+ [Tags] hello world
+ [Timeout] 1 min
+ No Operation
+
+Test setup and teardown
+ [Setup] Log Library keyword
+ Log Body
+ [Teardown] User Keyword
+
+Keyword Keyword documentation, tags and timeout
+ Keyword documentation, tags and timeout
+
+Keyword setup and teardown
+ Keyword setup and teardown
+
+Failure
+ [Documentation] FAIL Expected!
+ Fail Expected!
+ Fail Not run
+
+VAR
+ VAR ${x} x scope=SUITE
+
+IF
+ IF $x == 'y'
+ Fail Not run
+ ELSE IF $x == 'x'
+ Log Hi!
+ ELSE
+ Fail Not run
+ END
+
+TRY
+ TRY
+ Fail Hello!
+ EXCEPT no match here
+ Fail Not run
+ EXCEPT *! type=GLOB AS ${err}
+ Should Be Equal ${err} Hello!
+ ELSE
+ Fail Not run
+ FINALLY
+ Log Finally in FINALLY
+ END
+
+FOR and CONTINUE
+ FOR ${x} IN a b c
+ IF $x in ['a', 'c'] CONTINUE
+ Should Be Equal ${x} b
+ END
+ FOR ${i} ${x} IN ENUMERATE x start=1
+ Should Be Equal ${x}${i} x1
+ END
+ FOR ${i} ${x} IN ZIP ${{[]}} ${{['x']}} mode=LONGEST fill=1
+ Should Be Equal ${x}${i} x1
+ END
+
+WHILE and BREAK
+ WHILE True
+ BREAK
+ END
+ WHILE limit=1 on_limit=PASS on_limit_message=xxx
+ Log Run once
+ END
+
+GROUP
+ GROUP Named
+ Log Hello!
+ END
+ GROUP
+ Log Hello, again!
+ END
+
+Syntax error
+ [Documentation] FAIL Non-existing setting 'Bad'.
+ [Bad] Setting
+ [Ooops] I did it again
+
+*** Keywords ***
+User keyword
+ [Arguments] ${arg}=value
+ Should Be Equal ${arg} value
+ RETURN return ${arg}
+
+Keyword documentation, tags and timeout
+ [Documentation] Hello, world!
+ [Tags] hello world
+ [Timeout] 1 day
+ No Operation
+
+Keyword setup and teardown
+ [Setup] Log Library keyword
+ Log Body
+ [Teardown] User Keyword
diff --git a/atest/testdata/misc/example.resource b/atest/testdata/misc/example.resource
new file mode 100644
index 00000000000..989be6aa571
--- /dev/null
+++ b/atest/testdata/misc/example.resource
@@ -0,0 +1,3 @@
+*** Keywords ***
+Resource Keyword
+ Log Hello, resource!
diff --git a/atest/testdata/misc/non_ascii.robot b/atest/testdata/misc/non_ascii.robot
index eacd14c1c5f..6885b3c0e8b 100644
--- a/atest/testdata/misc/non_ascii.robot
+++ b/atest/testdata/misc/non_ascii.robot
@@ -8,7 +8,7 @@ Non-ASCII Log Messages
Sleep 0.001
Non-ASCII Return Value
- ${msg} = Evaluate u'Fran\\xe7ais'
+ ${msg} = Evaluate 'Fran\\xe7ais'
Should Be Equal ${msg} Français
Log ${msg}
diff --git a/atest/testdata/misc/pass_and_fail.robot b/atest/testdata/misc/pass_and_fail.robot
index a167233d026..b56050dc838 100644
--- a/atest/testdata/misc/pass_and_fail.robot
+++ b/atest/testdata/misc/pass_and_fail.robot
@@ -1,8 +1,11 @@
*** Settings ***
Documentation Some tests here
Suite Setup My Keyword Suite Setup
+Test Teardown Log Teardown!
Test Tags force
Library String
+Resource example.resource
+Variables variables.py arg ${1}
*** Variables ***
${LEVEL1} INFO
@@ -13,6 +16,8 @@ Pass
[Tags] pass
# I am a comment. Please ignore me.
My Keyword Pass
+ Resource Keyword
+ Should Be Equal ${VARIABLE} From variables.py with arg 1
Fail
[Documentation] FAIL Expected failure
diff --git a/atest/testdata/misc/setups_and_teardowns.robot b/atest/testdata/misc/setups_and_teardowns.robot
index a63799edc49..cb4c6f1ea3e 100644
--- a/atest/testdata/misc/setups_and_teardowns.robot
+++ b/atest/testdata/misc/setups_and_teardowns.robot
@@ -5,6 +5,7 @@ Suite Setup ${SUITE SETUP}
Suite Teardown ${SUITE TEARDOWN}
Test Setup ${TEST SETUP}
Test Teardown ${TEST TEARDOWN}
+Test Tags tag1 tag2
*** Variables ***
${SUITE SETUP} Suite Setup
@@ -20,6 +21,7 @@ Test with failing setup
[Documentation] FAIL
... Setup failed:
... Test Setup
+ [Tags] -tag2
[Setup] Fail Test Setup
Fail Should not be executed
diff --git a/atest/testdata/misc/variables.py b/atest/testdata/misc/variables.py
new file mode 100644
index 00000000000..c567d986c1f
--- /dev/null
+++ b/atest/testdata/misc/variables.py
@@ -0,0 +1,2 @@
+def get_variables(arg):
+ return {"VARIABLE": f"From variables.py with {arg}"}
diff --git a/atest/testdata/output/flatten_keywords.robot b/atest/testdata/output/flatten_keywords.robot
index c0290fd0c19..048a2f72321 100644
--- a/atest/testdata/output/flatten_keywords.robot
+++ b/atest/testdata/output/flatten_keywords.robot
@@ -32,11 +32,15 @@ Flatten controls in keyword
*** Keywords ***
Keyword 3
[Documentation] Doc of keyword 3
+ [Tags] kw3
+ [Timeout] 3 minutes
Log 3
Keyword 2
Keyword 2
[Documentation] Doc of keyword 2
+ [Tags] kw2
+ [Timeout] 2m
Log 2
Keyword 1
@@ -53,16 +57,16 @@ Keyword calling others
Keyword with tags not flatten
[Documentation] Doc of keyword not flatten
- [Tags] hello kitty
+ [Tags] hello kitty
Keyword 1
Keyword with tags and message flatten
[Documentation] Doc of flat keyword.
- [Tags] hello flatten
+ [Tags] hello flatten
Keyword 1 error=Expected e& 0
Log WHILE: ${i}
@@ -99,6 +105,11 @@ Flatten controls in keyword
FINALLY
Log finally
END
+ GROUP
+ Log Inside GROUP
+ END
+ VAR ${x} Using VAR
+ RETURN return value
Countdown
[Arguments] ${count}=${3}
diff --git a/atest/testdata/output/listener_interface/LibraryWithFailingListener.py b/atest/testdata/output/listener_interface/LibraryWithFailingListener.py
index ee45285d9d5..cc4df2c5015 100644
--- a/atest/testdata/output/listener_interface/LibraryWithFailingListener.py
+++ b/atest/testdata/output/listener_interface/LibraryWithFailingListener.py
@@ -1,4 +1,3 @@
import failing_listener
-
ROBOT_LIBRARY_LISTENER = failing_listener
diff --git a/atest/testdata/output/listener_interface/LinenoAndSource.py b/atest/testdata/output/listener_interface/LinenoAndSource.py
index 8f93e904c91..13a0c5156e8 100644
--- a/atest/testdata/output/listener_interface/LinenoAndSource.py
+++ b/atest/testdata/output/listener_interface/LinenoAndSource.py
@@ -1,50 +1,62 @@
import os
import tempfile
+from pathlib import Path
-
-TEMPDIR = os.getenv('TEMPDIR', tempfile.gettempdir())
+TEMPDIR = Path(os.getenv("TEMPDIR", tempfile.gettempdir()))
class LinenoAndSource:
ROBOT_LISTENER_API_VERSION = 2
def __init__(self):
- self.suite_output = open(os.path.join(TEMPDIR, 'LinenoAndSourceSuite.txt'), 'w')
- self.test_output = open(os.path.join(TEMPDIR, 'LinenoAndSourceTests.txt'), 'w')
+ self.suite_output = self._open("LinenoAndSourceSuite.txt")
+ self.test_output = self._open("LinenoAndSourceTests.txt")
self.output = None
+ def _open(self, name):
+ return open(TEMPDIR / name, "w", encoding="UTF-8")
+
def start_suite(self, name, attrs):
self.output = self.suite_output
- self.report('START', type='SUITE', name=name, **attrs)
+ self.report("START", type="SUITE", name=name, **attrs)
def end_suite(self, name, attrs):
self.output = self.suite_output
- self.report('END', type='SUITE', name=name, **attrs)
+ self.report("END", type="SUITE", name=name, **attrs)
def start_test(self, name, attrs):
self.output = self.test_output
- self.report('START', type='TEST', name=name, **attrs)
- self.output = open(os.path.join(TEMPDIR, name + '.txt'), 'w')
+ self.report("START", type="TEST", name=name, **attrs)
+ self.output = self._open(name + ".txt")
def end_test(self, name, attrs):
self.output.close()
self.output = self.test_output
- self.report('END', type='TEST', name=name, **attrs)
+ self.report("END", type="TEST", name=name, **attrs)
self.output = self.suite_output
def start_keyword(self, name, attrs):
- self.report('START', **attrs)
+ self.report("START", **attrs)
def end_keyword(self, name, attrs):
- self.report('END', **attrs)
+ self.report("END", **attrs)
def close(self):
self.suite_output.close()
self.test_output.close()
- def report(self, event, type, source, lineno=-1, name=None, kwname=None,
- status=None, **ignore):
- info = [event, type, (name or kwname).replace(' ', ' '), lineno, source]
+ def report(
+ self,
+ event,
+ type,
+ source,
+ lineno=-1,
+ name=None,
+ kwname=None,
+ status=None,
+ **ignore,
+ ):
+ info = [event, type, (name or kwname).replace(" ", " "), lineno, source]
if status:
info.append(status)
- self.output.write('\t'.join(str(i) for i in info) + '\n')
+ self.output.write("\t".join(str(i) for i in info) + "\n")
diff --git a/atest/testdata/output/listener_interface/ListenerOrder.py b/atest/testdata/output/listener_interface/ListenerOrder.py
new file mode 100644
index 00000000000..5b6e0bc0140
--- /dev/null
+++ b/atest/testdata/output/listener_interface/ListenerOrder.py
@@ -0,0 +1,31 @@
+import os
+import tempfile
+from pathlib import Path
+
+from robot.api.deco import library
+
+
+@library(listener="SELF", scope="GLOBAL")
+class ListenerOrder:
+ tempdir = Path(os.getenv("TEMPDIR", tempfile.gettempdir()))
+
+ def __init__(self, name, priority=None):
+ if priority is not None:
+ self.ROBOT_LISTENER_PRIORITY = priority
+ self.name = f"{name} ({priority})"
+
+ def start_suite(self, data, result):
+ self._write("start_suite")
+
+ def log_message(self, msg):
+ self._write("log_message")
+
+ def end_test(self, data, result):
+ self._write("end_test")
+
+ def close(self):
+ self._write("close", "listener_close_order.log")
+
+ def _write(self, msg, name="listener_order.log"):
+ with open(self.tempdir / name, "a", encoding="ASCII") as file:
+ file.write(f"{self.name}: {msg}\n")
diff --git a/atest/testdata/output/listener_interface/Recursion.py b/atest/testdata/output/listener_interface/Recursion.py
new file mode 100644
index 00000000000..b809cf3ea49
--- /dev/null
+++ b/atest/testdata/output/listener_interface/Recursion.py
@@ -0,0 +1,33 @@
+from robot.api import logger
+from robot.libraries.BuiltIn import BuiltIn
+
+run_keyword = BuiltIn().run_keyword
+
+
+def start_keyword(data, result):
+ message = result.args[0]
+ if message.startswith("Limited "):
+ limit = int(message.split()[1]) - 1
+ if limit > 0:
+ run_keyword("Log", f"Limited {limit} (by start_keyword)")
+ if message == "Unlimited in start_keyword":
+ run_keyword("Log", message)
+
+
+def end_keyword(data, result):
+ message = result.args[0]
+ if message.startswith("Limited "):
+ limit = int(message.split()[1]) - 1
+ if limit > 0:
+ run_keyword("Log", f"Limited {limit} (by end_keyword)")
+ if message == "Unlimited in end_keyword":
+ run_keyword("Log", message)
+
+
+def log_message(msg):
+ if msg.message.startswith("Limited "):
+ limit = int(msg.message.split()[1]) - 1
+ if limit > 0:
+ logger.info(f"Limited {limit} (by log_message)")
+ if msg.message == "Unlimited in log_message":
+ logger.info(msg.message)
diff --git a/atest/testdata/output/listener_interface/ResultModel.py b/atest/testdata/output/listener_interface/ResultModel.py
new file mode 100644
index 00000000000..56814976830
--- /dev/null
+++ b/atest/testdata/output/listener_interface/ResultModel.py
@@ -0,0 +1,50 @@
+from pathlib import Path
+
+from robot.api import logger
+from robot.api.interfaces import ListenerV3
+
+
+class ResultModel(ListenerV3):
+
+ def __init__(self, model_file: Path):
+ self.model_file = model_file
+ self.item_stack = []
+
+ def start_suite(self, data, result):
+ self.item_stack.append([])
+
+ def end_suite(self, data, result):
+ self.item_stack.pop()
+
+ def start_test(self, data, result):
+ self.item_stack.append([])
+ logger.info("Starting TEST")
+
+ def end_test(self, data, result):
+ logger.info("Ending TEST")
+ self._verify_body(result)
+ result.to_json(self.model_file)
+
+ def start_body_item(self, data, result):
+ self.item_stack[-1].append(result)
+ self.item_stack.append([])
+ logger.info(f"Starting {data.type}")
+
+ def end_body_item(self, data, result):
+ logger.info(f"Ending {data.type}")
+ self._verify_body(result)
+
+ def log_message(self, message):
+ if message.message == "Remove me!":
+ message.message = None
+ else:
+ self.item_stack[-1].append(message)
+
+ def _verify_body(self, result):
+ actual = list(result.body)
+ expected = self.item_stack.pop()
+ if actual != expected:
+ raise AssertionError(
+ f"Body of {result} was not expected.\n"
+ f"Got : {actual}\nExpected: {expected}"
+ )
diff --git a/atest/testdata/output/listener_interface/RunKeywordWithNonStringArguments.py b/atest/testdata/output/listener_interface/RunKeywordWithNonStringArguments.py
index c3de861da4c..079353d9bfb 100644
--- a/atest/testdata/output/listener_interface/RunKeywordWithNonStringArguments.py
+++ b/atest/testdata/output/listener_interface/RunKeywordWithNonStringArguments.py
@@ -2,4 +2,4 @@
def run_keyword_with_non_string_arguments():
- return BuiltIn().run_keyword('Create List', 1, 2, None)
+ return BuiltIn().run_keyword("Create List", 1, 2, None)
diff --git a/atest/testdata/output/listener_interface/body_items_v3/ArgumentModifier.py b/atest/testdata/output/listener_interface/body_items_v3/ArgumentModifier.py
index 069a17f001f..9bcae7353f6 100644
--- a/atest/testdata/output/listener_interface/body_items_v3/ArgumentModifier.py
+++ b/atest/testdata/output/listener_interface/body_items_v3/ArgumentModifier.py
@@ -8,62 +8,87 @@ def __init__(self, attr):
self.attr = attr
def __str__(self):
- return f'Object({self.attr!r})'
+ return f"Object({self.attr!r})"
class ArgumentModifier(ListenerV3):
- def start_library_keyword(self, data: running.Keyword,
- implementation: running.LibraryKeyword,
- result: result.Keyword):
- if 'modified' in data.parent.tags:
+ def start_library_keyword(
+ self,
+ data: running.Keyword,
+ implementation: running.LibraryKeyword,
+ result: result.Keyword,
+ ):
+ if "modified" in data.parent.tags or not isinstance(
+ data.parent, running.TestCase
+ ):
return
test = data.parent.name
create_keyword = data.parent.body.create_keyword
- data.parent.tags.add('modified')
- result.parent.tags.add('robot:continue-on-failure')
+ data.parent.tags.add("modified")
+ result.parent.tags.add("robot:continue-on-failure")
- # Set arguments using strings. Need to handle `name=value` syntax, escaping,
- # etc. Variables are supported. Non-string arguments are accepted as-is.
- if test == 'Arguments as strings':
+ # Modify arguments.
+ if test == "Library keyword arguments":
+ implementation.owner.instance.state = "new"
# Need to modify both data and result with the current keyword.
- data.args = result.args = ['${STATE}', 'number=${123}', 'obj=None',
- r'escape=c:\\temp\\new']
+ data.args = result.args = [
+ "${STATE}",
+ "number=${123}",
+ "obj=None",
+ r"escape=c:\\temp\\new",
+ ]
# When adding a new keyword, we only need to care about data.
- create_keyword('Library keyword', ['new', '123', r'c:\\temp\\new', 'NONE'])
- implementation.owner.instance.state = 'new'
+ create_keyword("Library keyword", ["new", "123", r"c:\\temp\\new", "NONE"])
+ # RF 7.1 and newer support named arguments directly.
+ create_keyword(
+ "Library keyword",
+ args=["new"],
+ named_args={
+ "number": "${42}",
+ "escape": r"c:\\temp\\new",
+ "obj": Object(42),
+ },
+ )
+ create_keyword(
+ "Library keyword",
+ named_args={
+ "number": 1.0,
+ "escape": r"c:\\temp\\new",
+ "obj": Object(1),
+ "state": "new",
+ },
+ )
+ create_keyword("Non-existing", args=["p"], named_args={"n": 1})
- # Set arguments using tuples (and strings). With tuples named-arguments
- # are handled automatically, but escaping needs to be handled separately.
- # Variables are supported.
- if test == 'Arguments as tuples':
- data.args = result.args = [('${STATE}',), ('escape', r'c:\\temp\\new'),
- ('obj', Object(123)), ('number', '${123}')]
- create_keyword('Library keyword', [('new',), 1.0, ('obj', Object(1)),
- r'escape=c:\\temp\\new'])
- implementation.owner.instance.state = 'new'
+ # Test that modified arguments are validated.
+ if test == "Too many arguments":
+ data.args = result.args = list("abcdefg")
+ create_keyword("Library keyword", list(range(100)))
+ if test == "Conversion error":
+ data.args = result.args = ["whatever", "not a number"]
+ create_keyword("Library keyword", ["number=bad"])
+ if test == "Positional after named":
+ data.args = result.args = ["positional", "number=-1", "ooops"]
- # Set arguments directly as a list of positional arguments and a dictionary
- # of named arguments. Variables are not supported and there's no need for
- # escaping. Argument conversion and validation is done.
- if test == 'Arguments directly as positional and named':
- data.args = result.args = (['${XXX}', '456', r'c:\temp\new'],
- {'obj': Object(456)})
- create_keyword('Library keyword', [(), {'state': '${XXX}', 'obj': Object(1),
- 'number': '1.0',
- 'escape': r'c:\temp\new'}])
- implementation.owner.instance.state = '${XXX}'
+ def start_user_keyword(
+ self,
+ data: running.Keyword,
+ implementation: running.UserKeyword,
+ result: result.Keyword,
+ ):
- # Test that modified arguments are validated.
- if test == 'Too many arguments':
- data.args = result.args = list('abcdefg')
- create_keyword('Library keyword', [list(range(100)), {}])
- if test == 'Conversion error':
- data.args = result.args = ['whatever', 'not a number']
- create_keyword('Library keyword', [(), {'number': 'bad'}])
- if test == 'Named argument not matching':
- data.args = result.args = [(), {'no': 'match'}]
- create_keyword('Library keyword', [('o', 'k'), {'bad': 'name'}])
- if test == 'Positional after named':
- data.args = result.args = [('positional',), ('name', 'value'), ('ooops',)]
+ if data.parent.name == "User keyword arguments" and len(data.parent.body) == 1:
+ data.args = result.args = ["A", "B", "C", "D"]
+ data.parent.body.create_keyword(
+ "User keyword",
+ args=["A", "B"],
+ named_args={
+ "d": "D",
+ "c": '${{"c".upper()}}',
+ },
+ )
+
+ if data.parent.name == "Too many arguments":
+ data.args = result.args = list("abcdefg")
diff --git a/atest/testdata/output/listener_interface/body_items_v3/ChangeStatus.py b/atest/testdata/output/listener_interface/body_items_v3/ChangeStatus.py
new file mode 100644
index 00000000000..a69b361ebf4
--- /dev/null
+++ b/atest/testdata/output/listener_interface/body_items_v3/ChangeStatus.py
@@ -0,0 +1,29 @@
+def end_keyword(data, result):
+ if result.failed and result.message == "Pass me!":
+ result.passed = True
+ result.message = "Failure hidden!"
+ elif result.passed and "Fail me!" in result.args:
+ result.failed = True
+ result.message = "Ooops!!"
+ elif result.passed and "Silent fail!" in result.args:
+ result.failed = True
+ elif result.skipped:
+ result.failed = True
+ result.message = "Failing!"
+ elif result.message == "Skip me!":
+ result.skipped = True
+ result.message = "Skipping!"
+ elif result.not_run and "Fail me!" in result.args:
+ result.failed = True
+ result.message = "Failing without running!"
+ elif "Mark not run!" in result.args:
+ result.not_run = True
+ elif result.message == "Change me!" or result.name == "Change message":
+ result.message = "Changed!"
+
+
+def end_structure(data, result):
+ result.passed = True
+
+
+end_for = end_while = end_if = end_try = end_structure
diff --git a/atest/testdata/output/listener_interface/body_items_v3/Library.py b/atest/testdata/output/listener_interface/body_items_v3/Library.py
index 12315763b57..a0c8276d927 100644
--- a/atest/testdata/output/listener_interface/body_items_v3/Library.py
+++ b/atest/testdata/output/listener_interface/body_items_v3/Library.py
@@ -1,34 +1,42 @@
-from eventvalidators import (SeparateMethods, SeparateMethodsAlsoForKeywords,
- StartEndBobyItemOnly)
+from eventvalidators import (
+ SeparateMethods, SeparateMethodsAlsoForKeywords, StartEndBobyItemOnly
+)
class Library:
- ROBOT_LIBRARY_LISTENER = [StartEndBobyItemOnly(),
- SeparateMethods(),
- SeparateMethodsAlsoForKeywords()]
+ ROBOT_LIBRARY_LISTENER = [
+ StartEndBobyItemOnly(),
+ SeparateMethods(),
+ SeparateMethodsAlsoForKeywords(),
+ ]
def __init__(self, validate_events=False):
if not validate_events:
self.ROBOT_LIBRARY_LISTENER = []
- self.state = 'initial'
+ self.state = "initial"
- def library_keyword(self, state='initial', number: int = 42, escape=r'c:\temp\new',
- obj=None):
+ def library_keyword(
+ self, state="initial", number: int = 42, escape=r"c:\temp\new", obj=None
+ ):
if self.state != state:
- raise AssertionError(f"Expected state to be '{state}', "
- f"but it was '{self.state}'.")
+ raise AssertionError(
+ f"Expected state to be '{state}', but it was '{self.state}'."
+ )
if number <= 0 or not isinstance(number, int):
- raise AssertionError(f"Expected number to be a positive integer, "
- f"but it was '{number}'.")
- if escape != r'c:\temp\new':
- raise AssertionError(rf"Expected path to be 'c:\temp\new', "
- rf"but it was '{escape}'.")
+ raise AssertionError(
+ f"Expected number to be a positive integer, but it was '{number}'."
+ )
+ if escape != r"c:\temp\new":
+ raise AssertionError(
+ rf"Expected path to be 'c:\temp\new', " rf"but it was '{escape}'."
+ )
if obj is not None and obj.attr != number:
- raise AssertionError(f"Expected 'obj.attr' to be {number}, "
- f"but it was '{obj.attr}'.")
+ raise AssertionError(
+ f"Expected 'obj.attr' to be {number}, but it was '{obj.attr}'."
+ )
def validate_events(self):
for listener in self.ROBOT_LIBRARY_LISTENER:
listener.validate()
if not self.ROBOT_LIBRARY_LISTENER:
- print('Event validation not active.')
+ print("Event validation not active.")
diff --git a/atest/testdata/output/listener_interface/body_items_v3/Modifier.py b/atest/testdata/output/listener_interface/body_items_v3/Modifier.py
index e8f490ec795..1f49b47b163 100644
--- a/atest/testdata/output/listener_interface/body_items_v3/Modifier.py
+++ b/atest/testdata/output/listener_interface/body_items_v3/Modifier.py
@@ -2,106 +2,131 @@
class Modifier:
- modify_once = 'User keyword'
-
- def start_library_keyword(self, data: running.Keyword,
- implementation: running.LibraryKeyword,
- result: result.Keyword):
- if (isinstance(data.parent, running.TestCase)
- and data.parent.name == 'Library keyword'):
- implementation.owner.instance.state = 'set by listener'
-
- def start_user_keyword(self, data: running.Keyword,
- implementation: running.UserKeyword,
- result: result.Keyword):
+ modify_once = "User keyword"
+
+ def start_library_keyword(
+ self,
+ data: running.Keyword,
+ implementation: running.LibraryKeyword,
+ result: result.Keyword,
+ ):
+ if (
+ isinstance(data.parent, running.TestCase)
+ and data.parent.name == "Library keyword"
+ ):
+ implementation.owner.instance.state = "set by listener"
+
+ def start_user_keyword(
+ self,
+ data: running.Keyword,
+ implementation: running.UserKeyword,
+ result: result.Keyword,
+ ):
# Modifications to the current implementation only affect this call.
if data.name == self.modify_once:
- implementation.body[0].name = 'Fail'
- implementation.body[0].args = ['Failed by listener once!']
+ implementation.body[0].name = "Fail"
+ implementation.body[0].args = ["Failed by listener once!"]
self.modify_once = None
if not implementation.body:
- implementation.body.create_keyword('Log', ['Added by listener!'])
- # Modifications via 'owner' resource file are permanent.
- if not implementation.owner.find_keywords('Non-existing keyword'):
- kw = implementation.owner.keywords.create('Non-existing keyword')
- kw.body.create_keyword('Log', ['This keyword exists now!'])
- inv = implementation.owner.find_keywords('Invalid keyword', count=1)
- if 'fixed' not in inv.tags:
- inv.args = ['${valid}', '${args}']
- inv.tags.add('fixed')
+ implementation.body.create_keyword("Log", ["Added by listener!"])
+ # Modifications via `owner` resource file are permanent.
+ # Starting from RF 7.1, modifications like this are easier to do
+ # by implementing the `resource_import` listener method.
+ if not implementation.owner.find_keywords("Non-existing keyword"):
+ kw = implementation.owner.keywords.create("Non-existing keyword")
+ kw.body.create_keyword("Log", ["This keyword exists now!"])
+ inv = implementation.owner.find_keywords("Invalid keyword", count=1)
+ if "fixed" not in inv.tags:
+ inv.args = ["${valid}", "${args}"]
+ inv.tags.add("fixed")
inv.error = None
- if implementation.matches('INVALID KEYWORD'):
- data.args = ['args modified', 'args=by listener']
- result.args = ['${secret}']
- result.doc = 'Results can be modified!'
- result.tags.add('start')
+ if implementation.matches("INVALID KEYWORD"):
+ data.args = ["args modified", "args=by listener"]
+ result.args = ["${secret}"]
+ result.doc = "Results can be modified!"
+ result.tags.add("start")
def end_keyword(self, data: running.Keyword, result: result.Keyword):
- if 'start' in result.tags:
- result.tags.add('end')
- result.doc = result.doc[:-1] + ' both in start and end!'
-
- def start_invalid_keyword(self, data: running.Keyword,
- implementation: running.KeywordImplementation,
- result: result.Keyword):
- if implementation.name == 'Duplicate keyword':
+ if "start" in result.tags:
+ result.tags.add("end")
+ result.doc = result.doc[:-1] + " both in start and end!"
+
+ def start_invalid_keyword(
+ self,
+ data: running.Keyword,
+ implementation: running.KeywordImplementation,
+ result: result.Keyword,
+ ):
+ if implementation.name == "Duplicate keyword":
assert isinstance(implementation, running.UserKeyword)
implementation.error = None
- implementation.body.create_keyword('Log', ['Problem "fixed".'])
- if implementation.name == 'Non-existing keyword 2':
+ implementation.body.create_keyword("Log", ['Problem "fixed".'])
+ if implementation.name == "Non-existing keyword 2":
assert isinstance(implementation, running.InvalidKeyword)
implementation.error = None
def start_for(self, data: running.For, result: result.For):
data.body.clear()
- result.assign = ['secret']
+ result.assign = ["secret"]
- def start_for_iteration(self, data: running.ForIteration,
- result: result.ForIteration):
+ def start_for_iteration(
+ self,
+ data: running.ForIteration,
+ result: result.ForIteration,
+ ):
# Each iteration starts with original body.
assert not data.body
- if data.assign['${i}'] == 1:
- data.body = [{'name': 'Fail', 'args': ["Listener failed me at '${x}'!"]}]
- data.body.create_keyword('Log', ['${i}: ${x}'])
- result.assign['${x}'] = 'xxx'
+ if data.assign["${i}"] == 1:
+ data.body = [{"name": "Fail", "args": ["Listener failed me at '${x}'!"]}]
+ data.body.create_keyword("Log", ["${i}: ${x}"])
+ result.assign["${x}"] = "xxx"
def start_while(self, data: running.While, result: result.While):
- data.body.clear()
-
- def start_while_iteration(self, data: running.WhileIteration,
- result: result.WhileIteration):
- # Each iteration starts with original body.
- assert not data.body
- iterations = len(result.parent.body)
- name = 'Fail' if iterations == 10 else 'Log'
- data.body.create_keyword(name, [f'{name} at iteration {iterations}.'])
+ if data.parent.name == "WHILE":
+ data.body.clear()
+ if data.parent.name == "WHILE with modified limit":
+ data.limit = 2
+ data.on_limit = "PASS"
+ data.on_limit_message = "Modified limit message."
+
+ def start_while_iteration(
+ self,
+ data: running.WhileIteration,
+ result: result.WhileIteration,
+ ):
+ if data.parent.parent.name == "WHILE":
+ # Each iteration starts with original body.
+ assert not data.body
+ iterations = len(result.parent.body)
+ name = "Fail" if iterations == 10 else "Log"
+ data.body.create_keyword(name, [f"{name} at iteration {iterations}."])
def start_if(self, data: running.If, result: result.If):
- data.body[1].condition = 'False'
- data.body[2].body[0].args = ['Executed!']
+ data.body[1].condition = "False"
+ data.body[2].body[0].args = ["Executed!"]
def start_if_branch(self, data: running.IfBranch, result: result.IfBranch):
if data.type == data.ELSE:
assert result.status == result.NOT_SET
else:
assert result.status == result.NOT_RUN
- result.message = 'Secret message!'
+ result.message = "Secret message!"
def start_try(self, data: running.Try, result: result.Try):
- data.body[0].body[0].args = ['Not caught!']
- data.body[1].patterns = ['No match!']
+ data.body[0].body[0].args = ["Not caught!"]
+ data.body[1].patterns = ["No match!"]
data.body.pop()
def start_try_branch(self, data: running.TryBranch, result: result.TryBranch):
assert data.type != data.FINALLY
def start_var(self, data: running.Var, result: result.Var):
- if data.name == '${y}':
- data.value = 'VAR by listener'
- result.value = ['secret']
+ if data.name == "${y}":
+ data.value = "VAR by listener"
+ result.value = ["secret"]
def start_return(self, data: running.Return, result: running.Return):
- data.values = ['RETURN by listener']
+ data.values = ["RETURN by listener"]
def end_return(self, data: running.Return, result: running.Return):
- result.values = ['secret']
+ result.values = ["secret"]
diff --git a/atest/testdata/output/listener_interface/body_items_v3/README.rst b/atest/testdata/output/listener_interface/body_items_v3/README.rst
index 8b76364415d..ff79a612380 100644
--- a/atest/testdata/output/listener_interface/body_items_v3/README.rst
+++ b/atest/testdata/output/listener_interface/body_items_v3/README.rst
@@ -17,6 +17,8 @@ Contents
how they work.
- ``_ modifies keyword arguments and can be used with
``_.
+- ``_ modifies keyword statuses and can be used with
+ ``_. Requires Robot Framework 7.1.
Usage
-----
@@ -37,3 +39,7 @@ To validate that all listener methods are executed, use this::
To see how keyword arguments can be modified::
robot --listener ArgumentModifier.py keyword_arguments.robot
+
+To see how keyword statuses can be modified::
+
+ robot --listener ChangeStatus.py change_status.robot
diff --git a/atest/testdata/output/listener_interface/body_items_v3/change_status.robot b/atest/testdata/output/listener_interface/body_items_v3/change_status.robot
new file mode 100644
index 00000000000..b34a934ba77
--- /dev/null
+++ b/atest/testdata/output/listener_interface/body_items_v3/change_status.robot
@@ -0,0 +1,64 @@
+*** Test Cases ***
+Fail to pass
+ Fail Pass me!
+ Log I'm run.
+
+Pass to fail
+ [Documentation] FAIL Ooops!!
+ Log Fail me!
+ Log I'm not run.
+
+Pass to fail without a message
+ [Documentation] FAIL
+ Log Silent fail!
+ Log I'm not run.
+
+Skip to fail
+ [Documentation] FAIL Failing!
+ Skip Fail me!
+ Log I'm not run.
+
+Fail to skip
+ [Documentation] SKIP Skipping!
+ Fail Skip me!
+ Log I'm not run.
+
+Not run to fail
+ [Documentation] FAIL Several failures occurred:
+ ...
+ ... 1) Ooops!!
+ ...
+ ... 2) Failing without running!
+ Log Fail me!
+ Log I'm not run.
+ Log Fail me!
+ Log I'm not run.
+
+Pass and fail to not run
+ [Documentation] FAIL I fail!
+ Log Mark not run!
+ Fail Mark not run!
+ Fail I fail!
+
+Only message
+ [Documentation] FAIL Changed!
+ Fail Change me!
+ Change message
+
+Control structures
+ FOR ${x} IN RANGE 1000
+ Fail Handled on FOR level.
+ END
+ WHILE True
+ Fail Handled on WHILE level.
+ END
+ IF True
+ Fail Handled on IF/ELSE ROOT level.
+ ELSE
+ Log I'm not run.
+ END
+ TRY
+ Fail Handled on TRY/EXCEPT ROOT level.
+ EXCEPT No match
+ Log I'm not run.
+ END
diff --git a/atest/testdata/output/listener_interface/body_items_v3/eventvalidators.py b/atest/testdata/output/listener_interface/body_items_v3/eventvalidators.py
index de3a758668e..7bdd3191387 100644
--- a/atest/testdata/output/listener_interface/body_items_v3/eventvalidators.py
+++ b/atest/testdata/output/listener_interface/body_items_v3/eventvalidators.py
@@ -23,12 +23,13 @@ def __init__(self):
'ITERATION', 'CONTINUE',
'WHILE',
'ITERATION', 'BREAK',
+ 'VAR', 'WHILE', 'ITERATION', 'VAR', 'KEYWORD',
'VAR', 'VAR', 'KEYWORD',
'KEYWORD', 'KEYWORD', 'RETURN', 'KEYWORD',
'ERROR',
'KEYWORD', 'KEYWORD', 'KEYWORD', 'RETURN',
'TEARDOWN'
- ])
+ ]) # fmt: skip
self.started = []
self.errors = []
self.suite = ()
@@ -42,26 +43,29 @@ def start_suite(self, data, result):
def validate(self):
name = type(self).__name__
if self.errors:
- raise AssertionError(f'{len(self.errors)} errors in {name} listener:\n'
- + '\n'.join(self.errors))
+ errors = "\n".join(self.errors)
+ raise AssertionError(
+ f"{len(self.errors)} errors in {name} listener:\n{errors}"
+ )
if not self._started_events_are_consumed():
- raise AssertionError(f'Listener {name} has not consumed all started events: '
- f'{self.started}')
- print(f'*INFO* Listener {name} is OK.')
+ raise AssertionError(
+ f"Listener {name} has not consumed all started events: {self.started}"
+ )
+ print(f"*INFO* Listener {name} is OK.")
def _started_events_are_consumed(self):
if len(self.started) == 1:
data, result, implementation = self.started[0]
- if data.type == result.type == 'TEARDOWN':
+ if data.type == result.type == "TEARDOWN":
return True
return False
def validate_start(self, data, result, implementation=None):
event = next(self.events, None)
if data.type != result.type:
- self.error('Mismatching data and result types.')
+ self.error("Mismatching data and result types.")
if data.type != event:
- self.error(f'Expected event {event}, got {data.type}.')
+ self.error(f"Expected event {event}, got {data.type}.")
self.validate_parent(data, self.suite[0])
self.validate_parent(result, self.suite[1])
if implementation:
@@ -72,13 +76,16 @@ def validate_parent(self, model, root):
while model.parent:
model = model.parent
if model is not root:
- self.error(f'Unexpected root: {model}')
+ self.error(f"Unexpected root: {model}")
def validate_end(self, data, result, implementation=None):
start_data, start_result, start_implementation = self.started.pop()
- if (data is not start_data or result is not start_result
- or implementation is not start_implementation):
- self.error('Mismatching start/end arguments.')
+ if (
+ data is not start_data
+ or result is not start_result
+ or implementation is not start_implementation
+ ):
+ self.error("Mismatching start/end arguments.")
class StartEndBobyItemOnly(EventValidator):
@@ -177,46 +184,46 @@ def end_error(self, data, result):
self.validate_end(data, result)
def start_body_item(self, data, result):
- self.error('Should not be called.')
+ self.error("Should not be called.")
def end_body_item(self, data, result):
- self.error('Should not be called.')
+ self.error("Should not be called.")
class SeparateMethodsAlsoForKeywords(SeparateMethods):
def start_user_keyword(self, data, implementation, result):
if implementation.type != implementation.USER_KEYWORD:
- self.error('Invalid implementation type.')
+ self.error("Invalid implementation type.")
self.validate_start(data, result, implementation)
def endUserKeyword(self, data, implementation, result):
if implementation.type != implementation.USER_KEYWORD:
- self.error('Invalid implementation type.')
+ self.error("Invalid implementation type.")
self.validate_end(data, result, implementation)
def start_library_keyword(self, data, implementation, result):
if implementation.type != implementation.LIBRARY_KEYWORD:
- self.error('Invalid implementation type.')
+ self.error("Invalid implementation type.")
self.validate_start(data, result, implementation)
def end_library_keyword(self, data, implementation, result):
if implementation.type != implementation.LIBRARY_KEYWORD:
- self.error('Invalid implementation type.')
+ self.error("Invalid implementation type.")
self.validate_end(data, result, implementation)
def startInvalidKeyword(self, data, implementation, result):
if not implementation.error:
- self.error('Invalid implementation type.')
+ self.error("Invalid implementation type.")
self.validate_start(data, result, implementation)
def end_invalid_keyword(self, data, implementation, result):
if not implementation.error:
- self.error('Invalid implementation type.')
+ self.error("Invalid implementation type.")
self.validate_end(data, result, implementation)
def start_keyword(self, data, result):
- self.error('Should not be called.')
+ self.error("Should not be called.")
def end_keyword(self, data, result):
- self.error('Should not be called.')
+ self.error("Should not be called.")
diff --git a/atest/testdata/output/listener_interface/body_items_v3/keyword_arguments.robot b/atest/testdata/output/listener_interface/body_items_v3/keyword_arguments.robot
index e4ab59267bb..071ab5104ba 100644
--- a/atest/testdata/output/listener_interface/body_items_v3/keyword_arguments.robot
+++ b/atest/testdata/output/listener_interface/body_items_v3/keyword_arguments.robot
@@ -5,14 +5,12 @@ Library Library.py
${STATE} new
*** Test Cases ***
-Arguments as strings
+Library keyword arguments
+ [Documentation] FAIL No keyword with name 'Non-existing' found.
Library keyword initial args are overwritten
-Arguments as tuples
- Library keyword initial args are overwritten
-
-Arguments directly as positional and named
- Library keyword initial args are overwritten
+User keyword arguments
+ User keyword initial args are overwritten
Too many arguments
[Documentation] FAIL
@@ -20,8 +18,11 @@ Too many arguments
...
... 1) Keyword 'Library.Library Keyword' expected 0 to 4 arguments, got 7.
...
- ... 2) Keyword 'Library.Library Keyword' expected 0 to 4 arguments, got 100.
+ ... 2) Keyword 'User keyword' expected 4 arguments, got 7.
+ ...
+ ... 3) Keyword 'Library.Library Keyword' expected 0 to 4 arguments, got 100.
Library keyword initial args are overwritten
+ User keyword initial args are overwritten
Conversion error
[Documentation] FAIL
@@ -32,16 +33,12 @@ Conversion error
... 2) ValueError: Argument 'number' got value 'bad' that cannot be converted to integer.
Library keyword initial args are overwritten
-Named argument not matching
- [Documentation] FAIL
- ... Several failures occurred:
- ...
- ... 1) Keyword 'Library.Library Keyword' got unexpected named argument 'no'.
- ...
- ... 2) Keyword 'Library.Library Keyword' got unexpected named argument 'bad'.
- Library keyword initial args are overwritten
-
Positional after named
[Documentation] FAIL
... Keyword 'Library.Library Keyword' got positional argument after named arguments.
Library keyword initial args are overwritten
+
+*** Keywords ***
+User keyword
+ [Arguments] ${a} ${b} ${c} ${d}
+ Should be equal ${a}-${b}-${c}-${d} A-B-C-D
diff --git a/atest/testdata/output/listener_interface/body_items_v3/tests.robot b/atest/testdata/output/listener_interface/body_items_v3/tests.robot
index 31839b44462..a88a2f5a7ba 100644
--- a/atest/testdata/output/listener_interface/body_items_v3/tests.robot
+++ b/atest/testdata/output/listener_interface/body_items_v3/tests.robot
@@ -59,6 +59,16 @@ WHILE
BREAK
END
+WHILE with modified limit
+ [Documentation] FAIL
+ ... WHILE loop was aborted because it did not finish within the limit of 1 iterations. \
+ ... Use the 'limit' argument to increase or remove the limit if needed.
+ VAR ${x} ${0}
+ WHILE True limit=1
+ VAR ${x} ${x + 1}
+ END
+ Should Be Equal ${x} ${2}
+
VAR
VAR ${x} value
VAR ${y} value scope=suite
diff --git a/atest/testdata/output/listener_interface/failing_listener.py b/atest/testdata/output/listener_interface/failing_listener.py
index e44cef8b92b..4a90c4faf75 100644
--- a/atest/testdata/output/listener_interface/failing_listener.py
+++ b/atest/testdata/output/listener_interface/failing_listener.py
@@ -10,10 +10,22 @@ def __init__(self, name):
def __call__(self, *args, **kws):
if not self.failed:
self.failed = True
- raise AssertionError("Expected failure in %s!" % self.__name__)
+ raise AssertionError(f"Expected failure in {self.__name__}!")
-for name in ['start_suite', 'end_suite', 'start_test', 'end_test',
- 'start_keyword', 'end_keyword', 'log_message', 'message',
- 'output_file', 'log_file', 'report_file', 'debug_file', 'close']:
+for name in [
+ "start_suite",
+ "end_suite",
+ "start_test",
+ "end_test",
+ "start_keyword",
+ "end_keyword",
+ "log_message",
+ "message",
+ "output_file",
+ "log_file",
+ "report_file",
+ "debug_file",
+ "close",
+]:
globals()[name] = ListenerMethod(name)
diff --git a/atest/testdata/output/listener_interface/imports/vars.py b/atest/testdata/output/listener_interface/imports/vars.py
index be8ae4eb1f6..d48564ada1b 100644
--- a/atest/testdata/output/listener_interface/imports/vars.py
+++ b/atest/testdata/output/listener_interface/imports/vars.py
@@ -1,2 +1,2 @@
-def get_variables(name='MY_VAR', value='MY_VALUE'):
+def get_variables(name="MY_VAR", value="MY_VALUE"):
return {name: value}
diff --git a/atest/testdata/output/listener_interface/keyword_running_listener.py b/atest/testdata/output/listener_interface/keyword_running_listener.py
index 05bca023dc8..2f3ea7da32c 100644
--- a/atest/testdata/output/listener_interface/keyword_running_listener.py
+++ b/atest/testdata/output/listener_interface/keyword_running_listener.py
@@ -1,30 +1,34 @@
-ROBOT_LISTENER_API_VERSION = 2
-
-
from robot.libraries.BuiltIn import BuiltIn
+ROBOT_LISTENER_API_VERSION = 2
run_keyword = BuiltIn().run_keyword
def start_suite(name, attrs):
- run_keyword('Log', 'start_suite')
+ run_keyword("Log", "start_suite")
def end_suite(name, attrs):
- run_keyword('Log', 'end_suite')
+ run_keyword("Log", "end_suite")
def start_test(name, attrs):
- run_keyword('Log', 'start_test')
+ run_keyword("Log", "start_test")
def end_test(name, attrs):
- run_keyword('Log', 'end_test')
+ run_keyword("Log", "end_test")
def start_keyword(name, attrs):
- run_keyword('Log', 'start_keyword')
+ if not recursive(name, attrs["args"]):
+ run_keyword("Log", "start_keyword")
def end_keyword(name, attrs):
- run_keyword('Log', 'end_keyword')
+ if not recursive(name, attrs["args"]):
+ run_keyword("Log", "end_keyword")
+
+
+def recursive(name, args):
+ return name == "BuiltIn.Log" and args in (["start_keyword"], ["end_keyword"])
diff --git a/atest/testdata/output/listener_interface/listener_order.robot b/atest/testdata/output/listener_interface/listener_order.robot
new file mode 100644
index 00000000000..f7363b0bd41
--- /dev/null
+++ b/atest/testdata/output/listener_interface/listener_order.robot
@@ -0,0 +1,9 @@
+*** Settings ***
+Library ListenerOrder.py LIB 1 ${0} AS LIB 1
+Library ListenerOrder.py LIB 2 AS LIB 2
+Library ListenerOrder.py NOT USED bad AS BAD
+Library ListenerOrder.py LIB 3 999.9 AS LIB 3
+
+*** Test Cases ***
+Test
+ Log Hello, listeners!
diff --git a/atest/testdata/output/listener_interface/logging_listener.py b/atest/testdata/output/listener_interface/logging_listener.py
index 81d4c683c95..9a614509a4b 100644
--- a/atest/testdata/output/listener_interface/logging_listener.py
+++ b/atest/testdata/output/listener_interface/logging_listener.py
@@ -1,27 +1,55 @@
import logging
-from robot.api import logger
+from robot.api import logger
+from robot.libraries.BuiltIn import BuiltIn
ROBOT_LISTENER_API_VERSION = 2
+RECURSION = False
+
def get_logging_listener_method(name):
+
def listener_method(*args):
- if name in ['message', 'log_message']:
+ global RECURSION
+ if RECURSION:
+ return
+ RECURSION = True
+ if name in ["message", "log_message"]:
msg = args[0]
message = f"{name}: {msg['level']} {msg['message']}"
- elif name == 'start_keyword':
+ elif name == "start_keyword":
message = f"start {args[1]['type']}".lower()
- elif name == 'end_keyword':
+ elif name == "end_keyword":
message = f"end {args[1]['type']}".lower()
else:
message = name
logging.info(message)
logger.warn(message)
+ # `set_xxx_variable` methods log normally, but they shouldn't log
+ # if they are used by a listener when no keyword is started.
+ if name == "start_suite":
+ BuiltIn().set_suite_variable("${SUITE}", "value")
+ if name == "start_test":
+ BuiltIn().set_test_variable("${TEST}", "value")
+ RECURSION = False
+
return listener_method
-for name in ['start_suite', 'end_suite', 'start_test', 'end_test',
- 'start_keyword', 'end_keyword', 'log_message', 'message',
- 'output_file', 'log_file', 'report_file', 'debug_file', 'close']:
+for name in [
+ "start_suite",
+ "end_suite",
+ "start_test",
+ "end_test",
+ "start_keyword",
+ "end_keyword",
+ "log_message",
+ "message",
+ "output_file",
+ "log_file",
+ "report_file",
+ "debug_file",
+ "close",
+]:
globals()[name] = get_logging_listener_method(name)
diff --git a/atest/testdata/output/listener_interface/original_and_resolved_name_v2.py b/atest/testdata/output/listener_interface/original_and_resolved_name_v2.py
index ebf4875c757..4b6cb96215f 100644
--- a/atest/testdata/output/listener_interface/original_and_resolved_name_v2.py
+++ b/atest/testdata/output/listener_interface/original_and_resolved_name_v2.py
@@ -2,8 +2,8 @@
def startTest(name, info):
- print('[START] [original] %s [resolved] %s' % (info['originalname'], name))
+ print(f"[START] [original] {info['originalname']} [resolved] {name}")
def end_test(name, info):
- print('[END] [original] %s [resolved] %s' % (info['originalname'], name))
+ print(f"[END] [original] {info['originalname']} [resolved] {name}")
diff --git a/atest/testdata/output/listener_interface/original_and_resolved_name_v3.py b/atest/testdata/output/listener_interface/original_and_resolved_name_v3.py
index 29b520f4810..4ce49babcb1 100644
--- a/atest/testdata/output/listener_interface/original_and_resolved_name_v3.py
+++ b/atest/testdata/output/listener_interface/original_and_resolved_name_v3.py
@@ -2,8 +2,8 @@
def startTest(data, result):
- result.message = '[START] [original] %s [resolved] %s' % (data.name, result.name)
+ result.message = f"[START] [original] {data.name} [resolved] {result.name}"
def end_test(data, result):
- result.message += '\n[END] [original] %s [resolved] %s' % (data.name, result.name)
+ result.message += f"\n[END] [original] {data.name} [resolved] {result.name}"
diff --git a/atest/testdata/output/listener_interface/recursion.robot b/atest/testdata/output/listener_interface/recursion.robot
new file mode 100644
index 00000000000..4741d458478
--- /dev/null
+++ b/atest/testdata/output/listener_interface/recursion.robot
@@ -0,0 +1,8 @@
+*** Test Cases ***
+Limited recursion
+ Log Limited 3
+
+Unlimited recursion
+ Log Unlimited in start_keyword
+ Log Unlimited in end_keyword
+ Log Unlimited in log_message
diff --git a/atest/testdata/output/listener_interface/result_model.robot b/atest/testdata/output/listener_interface/result_model.robot
new file mode 100644
index 00000000000..91ffc6aa6a6
--- /dev/null
+++ b/atest/testdata/output/listener_interface/result_model.robot
@@ -0,0 +1,19 @@
+*** Settings ***
+Suite Setup Log Library keyword
+Suite Teardown User keyword
+
+*** Test Cases ***
+Test
+ Log Library keyword
+ User keyword
+ TRY
+ Wait Until Keyword Succeeds 1x 0s Fail Ooops!
+ EXCEPT Keyword 'Fail' failed after retrying 1 time. The last error was: Ooops!
+ No Operation
+ END
+
+*** Keywords ***
+User keyword
+ Log User keyword DEBUG
+ Log Not logged TRACE
+ Log Remove me!
diff --git a/atest/testdata/output/listener_interface/timeouting_listener.py b/atest/testdata/output/listener_interface/timeouting_listener.py
index b24db0d30c9..971e232e09d 100644
--- a/atest/testdata/output/listener_interface/timeouting_listener.py
+++ b/atest/testdata/output/listener_interface/timeouting_listener.py
@@ -1,4 +1,4 @@
-from robot.errors import TimeoutError
+from robot.errors import TimeoutExceeded
class timeouting_listener:
@@ -6,7 +6,7 @@ class timeouting_listener:
timeout = False
def start_keyword(self, name, info):
- self.timeout = name == 'BuiltIn.Log'
+ self.timeout = name == "BuiltIn.Log"
def end_keyword(self, name, info):
self.timeout = False
@@ -14,4 +14,4 @@ def end_keyword(self, name, info):
def log_message(self, message):
if self.timeout:
self.timeout = False
- raise TimeoutError('Emulated timeout inside log_message')
+ raise TimeoutExceeded("Emulated timeout inside log_message")
diff --git a/atest/testdata/output/listener_interface/v3.py b/atest/testdata/output/listener_interface/v3.py
index 2cc14495483..ccf5dab02f4 100644
--- a/atest/testdata/output/listener_interface/v3.py
+++ b/atest/testdata/output/listener_interface/v3.py
@@ -1,79 +1,83 @@
+import os.path
import sys
from robot.api import SuiteVisitor
-
-
-ROBOT_LISTENER_API_VERSION = 3
+from robot.utils.asserts import assert_equal
def start_suite(data, result):
- data.name = data.doc = result.name = 'Not visible in results'
- result.doc = (result.doc + ' [start suite]').strip()
- result.metadata['suite'] = '[start]'
- result.metadata['tests'] = ''
- result.metadata['number'] = 42
- assert len(data.tests) == 2
- assert len(result.tests) == 0
- data.tests.create(name='Added by start_suite')
+ data.name = data.doc = result.name = "Not visible in results"
+ result.doc = (result.doc + " [start suite]").strip()
+ result.metadata["suite"] = "[start]"
+ result.metadata["tests"] = ""
+ result.metadata["number"] = 42
+ assert_equal(len(data.tests), 2)
+ assert_equal(len(result.tests), 0)
+ data.tests.create(name="Added by start_suite")
data.visit(TestModifier())
def end_suite(data, result):
- assert len(data.tests) == 5, '%d tests, not 5' % len(data.tests)
- assert len(result.tests) == 5, '%d tests, not 5' % len(result.tests)
+ assert_equal(len(data.tests), 5)
+ assert_equal(len(result.tests), 5)
for test in result.tests:
if test.setup or test.body or test.teardown:
raise AssertionError(f"Result test '{test.name}' not cleared")
- assert data.name == data.doc == result.name == 'Not visible in results'
- assert result.doc.endswith('[start suite]')
- assert result.metadata['suite'] == '[start]'
- assert result.metadata['tests'] == 'xxxxx'
- assert result.metadata['number'] == '42'
- result.name += ' [end suite]'
- result.doc += ' [end suite]'
- result.metadata['suite'] += ' [end]'
+ assert data.name == data.doc == result.name == "Not visible in results"
+ assert result.doc.endswith("[start suite]")
+ assert_equal(result.metadata["suite"], "[start]")
+ assert_equal(result.metadata["tests"], "xxxxx")
+ assert_equal(result.metadata["number"], "42")
+ result.name += " [end suite]"
+ result.doc += " [end suite]"
+ result.metadata["suite"] += " [end]"
for test in result.tests:
- test.name = 'Not visible in reports'
- test.status = 'PASS' # Not visible in reports
+ test.name = "Not visible in reports"
+ test.status = "PASS" # Not visible in reports
def startTest(data, result):
- data.name = data.doc = result.name = 'Not visible in results'
- result.doc = (result.doc + ' [start test]').strip()
- result.tags.add('[start]')
- result.message = '[start]'
- result.parent.metadata['tests'] += 'x'
- data.body.create_keyword('No Operation')
- if data is data.parent.tests[-1] and 'dynamic' not in data.tags:
- new = data.parent.tests.create(name='Added by startTest',
- tags=['dynamic', 'start'])
- new.body.create_keyword(name='Fail', args=['Dynamically added!'])
+ data.name = data.doc = result.name = "Not visible in results"
+ result.doc = (result.doc + " [start test]").strip()
+ result.tags.add("[start]")
+ result.message = "[start]"
+ result.parent.metadata["tests"] += "x"
+ data.body.create_keyword("No Operation")
+ if data is data.parent.tests[-1] and "dynamic" not in data.tags:
+ new = data.parent.tests.create(
+ name="Added by startTest", tags=["dynamic", "start"]
+ )
+ new.body.create_keyword(name="Fail", args=["Dynamically added!"])
def end_test(data, result):
- result.name = 'Does not go to output.xml'
- result.doc += ' [end test]'
- result.tags.add('[end]')
+ result.name = "Does not go to output.xml"
+ result.doc += " [end test]"
+ result.tags.add("[end]")
result.passed = not result.passed
- result.message += ' [end]'
- if 'dynamic' in data.tags and 'start' in data.tags:
- new = data.parent.tests.create(name='Added by end_test',
- doc='Dynamic',
- tags=['dynamic', 'end'])
- new.body.create_keyword(name='Log', args=['Dynamically added!', 'INFO'])
- data.name = data.doc = 'Not visible in results'
+ result.message += " [end]"
+ if "dynamic" in data.tags and "start" in data.tags:
+ new = data.parent.tests.create(
+ name="Added by end_test", doc="Dynamic", tags=["dynamic", "end"]
+ )
+ new.body.create_keyword(name="Log", args=["Dynamically added!", "INFO"])
+ data.name = data.doc = "Not visible in results"
def log_message(msg):
- msg.message = msg.message.upper()
- msg.timestamp = '2015-12-16 15:51:20.141'
+ if msg.message == 'Hello says "Fail"!' or msg.level == "TRACE":
+ msg.message = None
+ else:
+ msg.message = msg.message.upper()
+ msg.timestamp = "2015-12-16 15:51:20.141"
message = log_message
def output_file(path):
- print(f"Output: {path.name}", file=sys.__stderr__)
+ name = path.name if path is not None else "None"
+ print(f"Output: {name}", file=sys.__stderr__)
def log_file(path):
@@ -92,6 +96,51 @@ def xunit_file(path):
print(f"Xunit: {path.name}", file=sys.__stderr__)
+def library_import(library, importer):
+ if library.name == "BuiltIn":
+ library.find_keywords("Log", count=1).doc = "Changed!"
+ assert_equal(importer.name, "BuiltIn")
+ assert_equal(importer.args, ())
+ assert_equal(importer.source, None)
+ assert_equal(importer.lineno, None)
+ assert_equal(importer.owner, None)
+ else:
+ assert_equal(library.name, "String")
+ assert_equal(importer.name, "String")
+ assert_equal(importer.args, ())
+ assert_equal(importer.source.name, "pass_and_fail.robot")
+ assert_equal(importer.lineno, 6)
+ print(f"Imported library '{library.name}' with {len(library.keywords)} keywords.")
+
+
+def resource_import(resource, importer):
+ assert_equal(resource.name, "example")
+ assert_equal(resource.source.name, "example.resource")
+ assert_equal(importer.name, "example.resource")
+ assert_equal(importer.args, ())
+ assert_equal(importer.source.name, "pass_and_fail.robot")
+ assert_equal(importer.lineno, 7)
+ kw = resource.find_keywords("Resource Keyword", count=1)
+ kw.body.create_keyword("New!")
+ new = resource.keywords.create("New!", doc="Dynamically created.")
+ new.body.create_keyword("Log", ["Hello, new keyword!"])
+ print(
+ f"Imported resource '{resource.name}' with {len(resource.keywords)} keywords."
+ )
+
+
+def variables_import(attrs, importer):
+ assert_equal(attrs["name"], "variables.py")
+ assert_equal(attrs["args"], ["arg 1"])
+ assert_equal(os.path.basename(attrs["source"]), "variables.py")
+ assert_equal(importer.name, "variables.py")
+ assert_equal(importer.args, ("arg ${1}",))
+ assert_equal(importer.source.name, "pass_and_fail.robot")
+ assert_equal(importer.lineno, 8)
+ assert_equal(importer.owner.owner.source.name, "pass_and_fail.robot")
+ print(f"Imported variables '{attrs['name']}' without much info.")
+
+
def close():
print("Close", file=sys.__stderr__)
@@ -99,13 +148,6 @@ def close():
class TestModifier(SuiteVisitor):
def visit_test(self, test):
- test.name += ' [start suite]'
- test.doc = (test.doc + ' [start suite]').strip()
- test.tags.add('[start suite]')
-
-
-def not_implemented(*args):
- raise SystemExit('Should not be called!')
-
-
-library_import = resource_import = variables_import = not_implemented
+ test.name += " [start suite]"
+ test.doc = (test.doc + " [start suite]").strip()
+ test.tags.add("[start suite]")
diff --git a/atest/testdata/output/listener_interface/verify_template_listener.py b/atest/testdata/output/listener_interface/verify_template_listener.py
index 51cad05a434..c24d3e2e2ad 100644
--- a/atest/testdata/output/listener_interface/verify_template_listener.py
+++ b/atest/testdata/output/listener_interface/verify_template_listener.py
@@ -2,11 +2,12 @@
ROBOT_LISTENER_API_VERSION = 2
+
def start_test(name, attrs):
- template = attrs['template']
- expected = attrs['doc']
+ template = attrs["template"]
+ expected = attrs["doc"]
if template != expected:
- sys.__stderr__.write("Expected template '%s' but got '%s'.\n"
- % (expected, template))
+ sys.__stderr__.write(f"Expected template '{expected}', got '{template}'.\n")
+
end_test = start_test
diff --git a/atest/testdata/parsing/custom/CustomParser.py b/atest/testdata/parsing/custom/CustomParser.py
index 61ba62cb734..3bd91a69c45 100644
--- a/atest/testdata/parsing/custom/CustomParser.py
+++ b/atest/testdata/parsing/custom/CustomParser.py
@@ -1,20 +1,26 @@
from pathlib import Path
+import custom
+
from robot.api import TestSuite
from robot.api.interfaces import Parser, TestDefaults
-import custom
-
class CustomParser(Parser):
- def __init__(self, extension='custom', parse=True, init=False, fail=False,
- bad_return=False):
- self.extension = extension.split(',') if extension else None
+ def __init__(
+ self,
+ extension="custom",
+ parse=True,
+ init=False,
+ fail=False,
+ bad_return=False,
+ ):
+ self.extension = extension.split(",") if extension else None
if not parse:
self.parse = None
if init:
- self.extension.append('init')
+ self.extension.append("init")
else:
self.parse_init = None
self.fail = fail
@@ -22,9 +28,9 @@ def __init__(self, extension='custom', parse=True, init=False, fail=False,
def parse(self, source: Path, defaults: TestDefaults) -> TestSuite:
if self.fail:
- raise TypeError('Ooops!')
+ raise TypeError("Ooops!")
if self.bad_return:
- return 'bad'
+ return "bad"
suite = custom.parse(source)
suite.name = TestSuite.name_from_source(source, self.extension)
for test in suite.tests:
@@ -33,11 +39,11 @@ def parse(self, source: Path, defaults: TestDefaults) -> TestSuite:
def parse_init(self, source: Path, defaults: TestDefaults) -> TestSuite:
if self.fail:
- raise TypeError('Ooops in init!')
+ raise TypeError("Ooops in init!")
if self.bad_return:
return 42
- defaults.tags = ['tag from init']
- defaults.setup = {'name': 'Log', 'args': ['setup from init']}
- defaults.teardown = {'name': 'Log', 'args': ['teardown from init']}
- defaults.timeout = '42s'
- return TestSuite(name='📁', source=source.parent, metadata={'Parser': 'Custom'})
+ defaults.tags = ["tag from init"]
+ defaults.setup = {"name": "Log", "args": ["setup from init"]}
+ defaults.teardown = {"name": "Log", "args": ["teardown from init"]}
+ defaults.timeout = "42s"
+ return TestSuite(name="📁", source=source.parent, metadata={"Parser": "Custom"})
diff --git a/atest/testdata/parsing/custom/custom.py b/atest/testdata/parsing/custom/custom.py
index 8f90ae11b7d..487434f58b0 100644
--- a/atest/testdata/parsing/custom/custom.py
+++ b/atest/testdata/parsing/custom/custom.py
@@ -2,19 +2,18 @@
from robot.api import TestSuite
-
-EXTENSION = 'CUSTOM'
-extension = 'ignored'
+EXTENSION = "CUSTOM"
+extension = "ignored"
def parse(source):
- suite = TestSuite(source=source, metadata={'Parser': 'Custom'})
- for line in source.read_text().splitlines():
- if not line or line[0] in ('*', '#'):
+ suite = TestSuite(source=source, metadata={"Parser": "Custom"})
+ for line in source.read_text(encoding="UTF-8").splitlines():
+ if not line or line[0] in ("*", "#"):
continue
- if line[0] != ' ':
+ if line[0] != " ":
suite.tests.create(name=line)
else:
- name, *args = re.split(r'\s{2,}', line.strip())
+ name, *args = re.split(r"\s{2,}", line.strip())
suite.tests[-1].body.create_keyword(name, args)
return suite
diff --git a/atest/testdata/parsing/data_formats/resources/variables.py b/atest/testdata/parsing/data_formats/resources/variables.py
index 0bef67c42ef..8518acd4a9f 100644
--- a/atest/testdata/parsing/data_formats/resources/variables.py
+++ b/atest/testdata/parsing/data_formats/resources/variables.py
@@ -1,4 +1,4 @@
file_var1 = -314
-file_var2 = 'file variable 2'
-LIST__file_listvar = [True, 3.14, 'Hello, world!!']
-escaping = '-c:\\temp-\t-\x00-${x}-'
+file_var2 = "file variable 2"
+LIST__file_listvar = [True, 3.14, "Hello, world!!"]
+escaping = "-c:\\temp-\t-\x00-${x}-"
diff --git a/atest/testdata/parsing/escaping_variables.py b/atest/testdata/parsing/escaping_variables.py
index 56ec8802288..e63f27ca9a4 100644
--- a/atest/testdata/parsing/escaping_variables.py
+++ b/atest/testdata/parsing/escaping_variables.py
@@ -1,15 +1,15 @@
-sp = ' '
-hash = '#'
-bs = '\\'
-tab = '\t'
-nl = '\n'
-cr = '\r'
-x00 = '\x00'
-xE4 = '\xE4'
-xFF = '\xFF'
-u2603 = '\u2603' # SNOWMAN
-uFFFF = '\uFFFF'
-U00010905 = '\U00010905' # PHOENICIAN LETTER WAU
-U0010FFFF = '\U0010FFFF' # Biggest valid Unicode character
-var = '${non_existing}'
-pipe = '|'
+sp = " "
+hash = "#"
+bs = "\\"
+tab = "\t"
+nl = "\n"
+cr = "\r"
+x00 = "\x00"
+xE4 = "\xe4"
+xFF = "\xff"
+u2603 = "\u2603" # SNOWMAN
+uFFFF = "\uffff"
+U00010905 = "\U00010905" # PHOENICIAN LETTER WAU
+U0010FFFF = "\U0010ffff" # Biggest valid Unicode character
+var = "${non_existing}"
+pipe = "|"
diff --git a/atest/testdata/parsing/non_ascii_spaces.robot b/atest/testdata/parsing/non_ascii_spaces.robot
index 4d5108e913e..068037a908d 100644
--- a/atest/testdata/parsing/non_ascii_spaces.robot
+++ b/atest/testdata/parsing/non_ascii_spaces.robot
@@ -1,3 +1,5 @@
+language: fr
+
*** Settings ***
Test Setup No-break space : :
Test Teardown Ogham space mark : :
@@ -51,6 +53,9 @@ Ideographic space
Should be equal ${arg} ${IDEOGRAPHIC SPACE}
Should be equal ${arg} ${IDEOGRAPHIC SPACE}
+Embedded ${args} and BDD prefixes
+ Should be equal ${args} args
+
*** Test Cases ***
In header
No operation
@@ -88,3 +93,8 @@ In inline ELSE IF
${y} = IF False Not run ELSE IF True Set Variable OGHAM
${z} = IF False Not run ELSE IF True Set Variable IDEOGRAPHIC
Should Be Equal ${x}:${y}:${z} NBSP:OGHAM:IDEOGRAPHIC
+
+With embedded arguments and BDD prefixes
+ Given embedded args and BDD prefixes
+ then embedded args and bdd prefixes
+ Étant Donné embedded args AND BDD prefixes
diff --git a/atest/testdata/parsing/translations/custom/custom.py b/atest/testdata/parsing/translations/custom/custom.py
index 9f971c5267f..ceea5278ff9 100644
--- a/atest/testdata/parsing/translations/custom/custom.py
+++ b/atest/testdata/parsing/translations/custom/custom.py
@@ -2,37 +2,37 @@
class Custom(Language):
- settings_header = 'H S'
- variables_header = 'H v'
- test_cases_header = 'h te'
- tasks_header = 'H Ta'
- keywords_header = 'H k'
- comments_header = 'h C'
- library_setting = 'L'
- resource_setting = 'R'
- variables_setting = 'V'
- name_setting = 'N'
- documentation_setting = 'D'
- metadata_setting = 'M'
- suite_setup_setting = 'S S'
- suite_teardown_setting = 'S T'
- test_setup_setting = 't s'
- task_setup_setting = 'ta s'
- test_teardown_setting = 'T tea'
- task_teardown_setting = 'TA tea'
- test_template_setting = 'T TEM'
- task_template_setting = 'TA TEM'
- test_timeout_setting = 't ti'
- task_timeout_setting = 'ta ti'
- test_tags_setting = 'T Ta'
- task_tags_setting = 'Ta Ta'
- keyword_tags_setting = 'K T'
- setup_setting = 'S'
- teardown_setting = 'TeA'
- template_setting = 'Tem'
- tags_setting = 'Ta'
- timeout_setting = 'ti'
- arguments_setting = 'A'
+ settings_header = "H S"
+ variables_header = "H v"
+ test_cases_header = "h te"
+ tasks_header = "H Ta"
+ keywords_header = "H k"
+ comments_header = "h C"
+ library_setting = "L"
+ resource_setting = "R"
+ variables_setting = "V"
+ name_setting = "N"
+ documentation_setting = "D"
+ metadata_setting = "M"
+ suite_setup_setting = "S S"
+ suite_teardown_setting = "S T"
+ test_setup_setting = "t s"
+ task_setup_setting = "ta s"
+ test_teardown_setting = "T tea"
+ task_teardown_setting = "TA tea"
+ test_template_setting = "T TEM"
+ task_template_setting = "TA TEM"
+ test_timeout_setting = "t ti"
+ task_timeout_setting = "ta ti"
+ test_tags_setting = "T Ta"
+ task_tags_setting = "Ta Ta"
+ keyword_tags_setting = "K T"
+ setup_setting = "S"
+ teardown_setting = "TeA"
+ template_setting = "Tem"
+ tags_setting = "Ta"
+ timeout_setting = "ti"
+ arguments_setting = "A"
given_prefix = set()
when_prefix = set()
then_prefix = set()
diff --git a/atest/testdata/parsing/translations/per_file_config/many.robot b/atest/testdata/parsing/translations/per_file_config/many.robot
index bad92b36422..2ea228b8036 100644
--- a/atest/testdata/parsing/translations/per_file_config/many.robot
+++ b/atest/testdata/parsing/translations/per_file_config/many.robot
@@ -1,8 +1,8 @@
Language: DE
-LANGUAGE: Brazilian Portuguese
+LANGUAGE: Brazilian Portuguese
language: invalid
-language: bad again but not recognized due to this text
+language: another invalid value
language: THAI # comment here is fine
language:ukrainian
diff --git a/atest/testdata/parsing/variables.py b/atest/testdata/parsing/variables.py
index a53655ccb1d..af2e94f0eb3 100644
--- a/atest/testdata/parsing/variables.py
+++ b/atest/testdata/parsing/variables.py
@@ -1 +1 @@
-variable_file = 'variable in variable file'
+variable_file = "variable in variable file"
diff --git a/atest/testdata/rpa/tasks2.robot b/atest/testdata/rpa/tasks2.robot
index f9a507e370b..d12a309d1dc 100644
--- a/atest/testdata/rpa/tasks2.robot
+++ b/atest/testdata/rpa/tasks2.robot
@@ -1,6 +1,9 @@
+*** Variables ***
+${RPA} True
+
*** Tasks ***
Passing
- No operation
+ Should Be Equal ${OPTIONS.rpa} ${RPA} type=bool
Failing
[Documentation] FAIL Error
diff --git a/atest/testdata/running/NonAsciiByteLibrary.py b/atest/testdata/running/NonAsciiByteLibrary.py
index 6d40ed1df75..75b8a0c36d9 100644
--- a/atest/testdata/running/NonAsciiByteLibrary.py
+++ b/atest/testdata/running/NonAsciiByteLibrary.py
@@ -1,11 +1,14 @@
def in_exception():
- raise Exception(b'hyv\xe4')
+ raise Exception(b"hyv\xe4")
+
def in_return_value():
- return b'ty\xf6paikka'
+ return b"ty\xf6paikka"
+
def in_message():
- print(b'\xe4iti')
+ print(b"\xe4iti")
+
def in_multiline_message():
- print(b'\xe4iti\nis\xe4')
+ print(b"\xe4iti\nis\xe4")
diff --git a/atest/testdata/running/StandardExceptions.py b/atest/testdata/running/StandardExceptions.py
index 094c15bd550..9e791dd640e 100644
--- a/atest/testdata/running/StandardExceptions.py
+++ b/atest/testdata/running/StandardExceptions.py
@@ -1,9 +1,9 @@
-from robot.api import Failure, Error
+from robot.api import Error, Failure
-def failure(msg='I failed my duties', html=False):
+def failure(msg="I failed my duties", html=False):
raise Failure(msg, html)
-def error(msg='I errored my duties', html=False):
+def error(msg="I errored my duties", html=False):
raise Error(msg, html=html)
diff --git a/atest/testdata/running/continue_on_failure_tag.robot b/atest/testdata/running/continue_on_failure_tag.robot
index e8b888a6963..4fca8f3a2a2 100644
--- a/atest/testdata/running/continue_on_failure_tag.robot
+++ b/atest/testdata/running/continue_on_failure_tag.robot
@@ -1,95 +1,113 @@
*** Settings ***
-Library Exceptions
+Library Exceptions
*** Variables ***
-${HEADER} Several failures occurred:
-${EXC} ContinuableApocalypseException
+${HEADER} Several failures occurred:\n
+${EXC} ContinuableApocalypseException
+${FAILURE} failure
*** Test Cases ***
Continue in test with continue tag
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) 1\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) 1
+ ...
... 2) 2
- [Tags] robot:continue-on-failure
- Fail 1
- Fail 2
+ [Tags] robot:continue-on-failure
+ Fail 1
+ Fail 2
Log This should be executed
Continue in test with Set Tags
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) 1\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) 1
+ ...
... 2) 2
- Set Tags ROBOT:CONTINUE-ON-FAILURE # case shouldn't matter
- Fail 1
- Fail 2
+ Set Tags ROBOT:CONTINUE-ON-${FAILURE} # Case doesn't matter and variables work.
+ Fail 1
+ Fail 2
Log This should be executed
Continue in user keyword with continue tag
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) kw1a\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) kw1a
+ ...
... 2) kw1b
Failure in user keyword with continue tag
- Fail This should not be executed
+ Fail This should not be executed
Continue in test with continue tag and UK without tag
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) kw2a\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) kw2a
+ ...
... 2) This should be executed
- [Tags] robot:CONTINUE-on-failure # case shouldn't matter
+ [Tags] robot:CONTINUE-on-${FAILURE} # Case doesn't matter and variables work.
Failure in user keyword without tag
- Fail This should be executed
+ Fail This should be executed
Continue in test with continue tag and nested UK with and without tag
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) kw1a\n\n
- ... 2) kw1b\n\n
- ... 3) kw2a\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) kw1a
+ ...
+ ... 2) kw1b
+ ...
+ ... 3) kw2a
+ ...
... 4) This should be executed
- [Tags] robot: continue-on-failure # spaces should be collapsed
- Failure in user keyword with continue tag run_kw=Failure in user keyword without tag
- Fail This should be executed
+ [Tags] robot: continue-on-failure # Spaces are collapesed.
+ Failure in user keyword with continue tag run_kw=Failure in user keyword without tag
+ Fail This should be executed
Continue in test with continue tag and two nested UK with continue tag
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) kw1a\n\n
- ... 2) kw1b\n\n
- ... 3) kw1a\n\n
- ... 4) kw1b\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) kw1a
+ ...
+ ... 2) kw1b
+ ...
+ ... 3) kw1a
+ ...
+ ... 4) kw1b
+ ...
... 5) This should be executed
- [Tags] robot:continue-on-failure
- Failure in user keyword with continue tag run_kw=Failure in user keyword with continue tag
- Fail This should be executed
+ [Tags] robot:continue-on-failure
+ Failure in user keyword with continue tag run_kw=Failure in user keyword with continue tag
+ Fail This should be executed
Continue in FOR loop with continue tag
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) loop-1\n\n
- ... 2) loop-2\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) loop-1
+ ...
+ ... 2) loop-2
+ ...
... 3) loop-3
- [Tags] robot:continue-on-failure
+ [Tags] robot:continue-on-failure
FOR ${val} IN 1 2 3
- Fail loop-${val}
+ Fail loop-${val}
END
Continue in FOR loop with Set Tags
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) loop-1\n\n
- ... 2) loop-2\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) loop-1
+ ...
+ ... 2) loop-2
+ ...
... 3) loop-3
FOR ${val} IN 1 2 3
- Set Tags robot:continue-on-failure
- Fail loop-${val}
+ Set Tags robot:continue-on-failure
+ Fail loop-${val}
END
No continue in FOR loop without tag
[Documentation] FAIL loop-1
FOR ${val} IN 1 2 3
- Fail loop-${val}
+ Fail loop-${val}
END
Continue in FOR loop in UK with continue tag
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) kw-loop1-1\n\n
- ... 2) kw-loop1-2\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) kw-loop1-1
+ ...
+ ... 2) kw-loop1-2
+ ...
... 3) kw-loop1-3
FOR loop in in user keyword with continue tag
@@ -98,17 +116,20 @@ Continue in FOR loop in UK without tag
FOR loop in in user keyword without tag
Continue in IF with continue tag
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) 1\n\n
- ... 2) 2\n\n
- ... 3) 3\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) 1
+ ...
+ ... 2) 2
+ ...
+ ... 3) 3
+ ...
... 4) 4
- [Tags] robot:continue-on-failure
- IF 1==1
+ [Tags] robot:continue-on-failure
+ IF 1==1
Fail 1
Fail 2
END
- IF 1==2
+ IF 1==2
No Operation
ELSE
Fail 3
@@ -116,17 +137,19 @@ Continue in IF with continue tag
END
Continue in IF with set and remove tag
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) 1\n\n
- ... 2) 2\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) 1
+ ...
+ ... 2) 2
+ ...
... 3) 3
- Set Tags robot:continue-on-failure
- IF 1==1
+ Set Tags robot:continue-on-failure
+ IF 1==1
Fail 1
Fail 2
END
- Remove Tags robot:continue-on-failure
- IF 1==2
+ Remove Tags robot:continue-on-failure
+ IF 1==2
No Operation
ELSE
Fail 3
@@ -135,16 +158,19 @@ Continue in IF with set and remove tag
No continue in IF without tag
[Documentation] FAIL 1
- IF 1==1
+ IF 1==1
Fail 1
Fail This should not be executed
END
Continue in IF in UK with continue tag
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) kw7a\n\n
- ... 2) kw7b\n\n
- ... 3) kw7c\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) kw7a
+ ...
+ ... 2) kw7b
+ ...
+ ... 3) kw7c
+ ...
... 4) kw7d
IF in user keyword with continue tag
@@ -153,121 +179,144 @@ No continue in IF in UK without tag
IF in user keyword without tag
Continue in Run Keywords with continue tag
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) 1\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) 1
+ ...
... 2) 2
- [Tags] robot:continue-on-failure
- Run Keywords Fail 1 AND Fail 2
+ [Tags] robot:continue-on-failure
+ Run Keywords Fail 1 AND Fail 2
Recursive continue in test with continue tag and two nested UK without tag
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) kw2a\n\n
- ... 2) kw2b\n\n
- ... 3) kw2a\n\n
- ... 4) kw2b\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) kw2a
+ ...
+ ... 2) kw2b
+ ...
+ ... 3) kw2a
+ ...
+ ... 4) kw2b
+ ...
... 5) This should be executed
- [Tags] robot:recursive-continue-on-failure
- Failure in user keyword without tag run_kw=Failure in user keyword without tag
- Fail This should be executed
+ [Tags] robot:recursive-continue-on-failure
+ Failure in user keyword without tag run_kw=Failure in user keyword without tag
+ Fail This should be executed
Recursive continue in test with Set Tags and two nested UK without tag
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) kw2a\n\n
- ... 2) kw2b\n\n
- ... 3) kw2a\n\n
- ... 4) kw2b\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) kw2a
+ ...
+ ... 2) kw2b
+ ...
+ ... 3) kw2a
+ ...
+ ... 4) kw2b
+ ...
... 5) This should be executed
- Set Tags robot: recursive-continue-on-failure # spaces should be collapsed
- Failure in user keyword without tag run_kw=Failure in user keyword without tag
- Fail This should be executed
+ Set Tags robot: recursive-continue-on-failure # Spaces are collapsed.
+ Failure in user keyword without tag run_kw=Failure in user keyword without tag
+ Fail This should be executed
Recursive continue in test with continue tag and two nested UK with and without tag
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) kw1a\n\n
- ... 2) kw1b\n\n
- ... 3) kw2a\n\n
- ... 4) kw2b\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) kw1a
+ ...
+ ... 2) kw1b
+ ...
+ ... 3) kw2a
+ ...
+ ... 4) kw2b
+ ...
... 5) This should be executed
- [Tags] ROBOT:RECURSIVE-CONTINUE-ON-FAILURE # case shouldn't matter
- Failure in user keyword with continue tag run_kw=Failure in user keyword without tag
- Fail This should be executed
+ [Tags] ROBOT:RECURSIVE-CONTINUE-ON-${FAILURE} # Case doesn't matter and variables work.
+ Failure in user keyword with continue tag run_kw=Failure in user keyword without tag
+ Fail This should be executed
Recursive continue in test with continue tag and UK with stop tag
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) kw4a\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) kw4a
+ ...
... 2) This should be executed
- [Tags] robot:recursive-continue-on-failure
+ [Tags] robot:recursive-continue-on-failure
Failure in user keyword with stop tag
- Fail This should be executed
+ Fail This should be executed
Recursive continue in test with continue tag and UK with recursive stop tag
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) kw11a\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) kw11a
+ ...
... 2) This should be executed
- [Tags] robot:recursive-continue-on-failure
+ [Tags] robot:recursive-continue-on-failure
Failure in user keyword with recursive stop tag
- Fail This should be executed
+ Fail This should be executed
Recursive continue in user keyword
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) kw3a\n\n
- ... 2) kw3b\n\n
- ... 3) kw2a\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) kw3a
+ ...
+ ... 2) kw3b
+ ...
+ ... 3) kw2a
+ ...
... 4) kw2b
- Failure in user keyword with recursive continue tag run_kw=Failure in user keyword without tag
- Fail This should not be executed
+ Failure in user keyword with recursive continue tag run_kw=Failure in user keyword without tag
+ Fail This should not be executed
Recursive continue in nested keyword
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) kw3a\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) kw3a
+ ...
... 2) kw3b
- Failure in user keyword without tag run_kw=Failure in user keyword with recursive continue tag
- Fail This should not be executed
+ Failure in user keyword without tag run_kw=Failure in user keyword with recursive continue tag
+ Fail This should not be executed
stop-on-failure in keyword in Teardown
[Documentation] FAIL Teardown failed:\nkw4a
- [Teardown] Failure in user keyword with stop tag
No Operation
+ [Teardown] Failure in user keyword with stop tag
stop-on-failure with continuable failure in keyword in Teardown
- [Documentation] FAIL Teardown failed:\n${HEADER}\n\n
- ... 1) ${EXC}: kw9a\n\n
+ [Documentation] FAIL Teardown failed:\n${HEADER}
+ ... 1) ${EXC}: kw9a
+ ...
... 2) kw9b
- [Teardown] Continuable Failure in user keyword with stop tag
No Operation
+ [Teardown] Continuable Failure in user keyword with stop tag
stop-on-failure with run-kw-and-continue failure in keyword in Teardown
- [Documentation] FAIL Teardown failed:\n${HEADER}\n\n
- ... 1) kw10a\n\n
+ [Documentation] FAIL Teardown failed:\n${HEADER}
+ ... 1) kw10a
+ ...
... 2) kw10b
- [Teardown] run-kw-and-continue failure in user keyword with stop tag
No Operation
+ [Teardown] run-kw-and-continue failure in user keyword with stop tag
stop-on-failure with run-kw-and-continue failure in keyword
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) kw10a\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) kw10a
+ ...
... 2) kw10b
run-kw-and-continue failure in user keyword with stop tag
Test teardown using run keywords with stop tag in test case
[Documentation] FAIL Teardown failed:\n1
- [Tags] robot:stop-on-failure
- [Teardown] Run Keywords Fail 1 AND Fail 2
+ [Tags] robot:stop-on-failure
No Operation
+ [Teardown] Run Keywords Fail 1 AND Fail 2
Test teardown using user keyword with stop tag in test case
- [Documentation] FAIL Teardown failed:\n${HEADER}\n\n
- ... 1) kw2a\n\n
+ [Documentation] FAIL Teardown failed:\n${HEADER}
+ ... 1) kw2a
+ ...
... 2) kw2b
- [Tags] robot:stop-on-failure
- [Teardown] Failure in user keyword without tag
+ [Tags] robot:STOP-on-${FAILURE}
No Operation
+ [Teardown] Failure in user keyword without tag
Test teardown using user keyword with recursive stop tag in test case
[Documentation] FAIL Teardown failed:\nkw2a
- [Tags] robot:recursive-stop-on-failure
- [Teardown] Failure in user keyword without tag
+ [Tags] robot:recursive-stop-on-${FAILURE}
No Operation
+ [Teardown] Failure in user keyword without tag
Test Teardown with stop tag in user keyword
[Documentation] FAIL Keyword teardown failed:\nkw5a
@@ -279,22 +328,24 @@ Test Teardown with recursive stop tag in user keyword
Teardown with recursive stop tag in user keyword
Test Teardown with recursive stop tag and UK with continue tag
- # continue-on-failure overrides recursive-stop-on-failure
- [Documentation] FAIL Keyword teardown failed:\n${HEADER}\n\n
- ... 1) kw1a\n\n
+ [Documentation] Continue-on-failure overrides recursive-stop-on-failure.
+ ... FAIL Keyword teardown failed:\n${HEADER}
+ ... 1) kw1a
+ ...
... 2) kw1b
Teardown with recursive stop tag in user keyword run_kw=Failure in user keyword with continue tag
Test Teardown with recursive stop tag and UK with recursive continue tag
- # recursive-continue-on-failure overrides recursive-stop-on-failure
- [Documentation] FAIL Keyword teardown failed:\n${HEADER}\n\n
- ... 1) kw3a\n\n
+ [Documentation] Recursive-continue-on-failure overrides recursive-stop-on-failure.
+ ... FAIL Keyword teardown failed:\n${HEADER}
+ ... 1) kw3a
+ ...
... 2) kw3b
Teardown with recursive stop tag in user keyword run_kw=Failure in user keyword with recursive continue tag
stop-on-failure with Template
[Documentation] FAIL 42 != 43
- [Tags] robot:stop-on-failure
+ [Tags] robot:stop-on-failure
[Template] Should Be Equal
Same Same
42 43
@@ -302,49 +353,57 @@ stop-on-failure with Template
recursive-stop-on-failure with Template
[Documentation] FAIL 42 != 43
- [Tags] robot:recursive-stop-on-failure
+ [Tags] robot:recursive-stop-on-failure
[Template] Should Be Equal
Same Same
42 43
Something Different
stop-on-failure with Template and Teardown
- [Documentation] FAIL 42 != 43\n\nAlso teardown failed:\n1
- [Tags] robot:stop-on-failure
- [Teardown] Run Keywords Fail 1 AND Fail 2
+ [Documentation] FAIL 42 != 43
+ ...
+ ... Also teardown failed:
+ ... 1
+ [Tags] robot:stop-on-failure
[Template] Should Be Equal
Same Same
42 43
Something Different
+ [Teardown] Run Keywords Fail 1 AND Fail 2
stop-on-failure does not stop continuable failure in test
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) 1\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) 1
+ ...
... 2) 2
- [Tags] robot:stop-on-failure
+ [Tags] robot:stop-on-failure
Run Keyword And Continue On Failure Fail 1
Fail 2
Test recursive-continue-recursive-stop
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) kw11a\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) kw11a
+ ...
... 2) 2
- [Tags] robot:recursive-continue-on-failure
+ [Tags] robot: recursive-CONTINUE-on-${FAILURE}
Failure in user keyword with recursive stop tag
Fail 2
Test recursive-stop-recursive-continue
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) kw3a\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) kw3a
+ ...
... 2) kw3b
[Tags] robot:recursive-stop-on-failure
Failure in user keyword with recursive continue tag
Fail 2
Test recursive-stop-recursive-continue-recursive-stop
- [Documentation] FAIL ${HEADER}\n\n
- ... 1) kw3a\n\n
- ... 2) kw3b\n\n
+ [Documentation] FAIL ${HEADER}
+ ... 1) kw3a
+ ...
+ ... 2) kw3b
+ ...
... 3) kw11a
[Tags] robot:recursive-stop-on-failure
Failure in user keyword with recursive continue tag run_kw=Failure in user keyword with recursive stop tag
@@ -358,17 +417,16 @@ Test test setup with continue-on-failure
Fail should-not-run
Test test setup with recursive-continue-on-failure
- [Documentation] FAIL Setup failed:\n${HEADER}\n\n
- ... 1) setup-1\n\n
+ [Documentation] FAIL Setup failed:\n${HEADER}
+ ... 1) setup-1
+ ...
... 2) setup-2
[Tags] robot:recursive-continue-on-failure
[Setup] test setup
Fail should-not-run
recursive-stop-on-failure with continue-on-failure
- [Documentation] FAIL
- ... Several failures occurred:
- ...
+ [Documentation] FAIL ${HEADER}
... 1) 1.1.1
...
... 2) 2.1.1
@@ -394,9 +452,7 @@ recursive-stop-on-failure with continue-on-failure
[Teardown] recursive-stop-on-failure with continue-on-failure
recursive-continue-on-failure with stop-on-failure
- [Documentation] FAIL
- ... Several failures occurred:
- ...
+ [Documentation] FAIL ${HEADER}
... 1) 1.1.1
...
... 2) 1.1.2
@@ -406,8 +462,7 @@ recursive-continue-on-failure with stop-on-failure
... 4) 1.2.2
...
... Also teardown failed:
- ... Several failures occurred:
- ...
+ ... ${HEADER}
... 1) 1.1.1
...
... 2) 1.1.2
@@ -424,67 +479,67 @@ recursive-continue-on-failure with stop-on-failure
*** Keywords ***
Failure in user keyword with continue tag
[Arguments] ${run_kw}=No Operation
- [Tags] robot:continue-on-failure
- Fail kw1a
- Fail kw1b
+ [Tags] robot:continue-on-failure
+ Fail kw1a
+ Fail kw1b
Log This should be executed
- Run Keyword ${run_kw}
+ Run Keyword ${run_kw}
Failure in user keyword without tag
[Arguments] ${run_kw}=No Operation
- Run Keyword ${run_kw}
- Fail kw2a
- Fail kw2b
+ Run Keyword ${run_kw}
+ Fail kw2a
+ Fail kw2b
Failure in user keyword with recursive continue tag
[Arguments] ${run_kw}=No Operation
- [Tags] robot:recursive-continue-on-failure
- Fail kw3a
- Fail kw3b
+ [Tags] ROBOT:recursive-continue-on-${FAILURE}
+ Fail kw3a
+ Fail kw3b
Log This should be executed
- Run Keyword ${run_kw}
+ Run Keyword ${run_kw}
Failure in user keyword with stop tag
- [Tags] robot:stop-on-failure
- Fail kw4a
+ [Tags] robot:stop-on-failure
+ Fail kw4a
Log This should not be executed
- Fail kw4b
+ Fail kw4b
Failure in user keyword with recursive stop tag
[Tags] robot:recursive-stop-on-failure
Fail kw11a
- Log This is not executed
+ Log This is not executed
Fail kw11b
Teardown with stop tag in user keyword
- [Tags] robot:stop-on-failure
- [Teardown] Run Keywords Fail kw5a AND Fail kw5b
+ [Tags] robot:STOP-on-${FAILURE}
No Operation
+ [Teardown] Run Keywords Fail kw5a AND Fail kw5b
Teardown with recursive stop tag in user keyword
[Arguments] ${run_kw}=No Operation
- [Tags] robot:recursive-stop-on-failure
- [Teardown] Run Keywords ${run_kw} AND Fail kw6a AND Fail kw6b
+ [Tags] ROBOT:recursive-STOP-on-${FAILURE}
No Operation
+ [Teardown] Run Keywords ${run_kw} AND Fail kw6a AND Fail kw6b
FOR loop in in user keyword with continue tag
- [Tags] robot:continue-on-failure
+ [Tags] robot:continue-on-failure
FOR ${val} IN 1 2 3
- Fail kw-loop1-${val}
+ Fail kw-loop1-${val}
END
FOR loop in in user keyword without tag
FOR ${val} IN 1 2 3
- Fail kw-loop2-${val}
+ Fail kw-loop2-${val}
END
IF in user keyword with continue tag
- [Tags] robot:continue-on-failure
- IF 1==1
+ [Tags] ROBOT:continue-on-${FAILURE}
+ IF 1==1
Fail kw7a
Fail kw7b
END
- IF 1==2
+ IF 1==2
No Operation
ELSE
Fail kw7c
@@ -492,11 +547,11 @@ IF in user keyword with continue tag
END
IF in user keyword without tag
- IF 1==1
+ IF 1==1
Fail kw8a
Fail kw8b
END
- IF 1==2
+ IF 1==2
No Operation
ELSE
Fail kw8c
@@ -504,7 +559,7 @@ IF in user keyword without tag
END
Continuable Failure in user keyword with stop tag
- [Tags] robot:stop-on-failure
+ [Tags] robot:stop-on-failure
Raise Continuable Failure kw9a
Log This is executed
Fail kw9b
@@ -512,7 +567,7 @@ Continuable Failure in user keyword with stop tag
Fail kw9c
run-kw-and-continue failure in user keyword with stop tag
- [Tags] robot:stop-on-failure
+ [Tags] robot:stop-on-failure
Run Keyword And Continue On Failure Fail kw10a
Log This is executed
Fail kw10b
diff --git a/atest/testdata/running/prevent_recursion.robot b/atest/testdata/running/detect_recursion.robot
similarity index 94%
rename from atest/testdata/running/prevent_recursion.robot
rename to atest/testdata/running/detect_recursion.robot
index 9d4fc9e3e9e..ca1cda72244 100644
--- a/atest/testdata/running/prevent_recursion.robot
+++ b/atest/testdata/running/detect_recursion.robot
@@ -2,7 +2,7 @@
Suite Teardown Recursion With Run Keyword
*** Variables ***
-${LIMIT EXCEEDED} Maximum limit of started keywords and control structures exceeded.
+${LIMIT EXCEEDED} Recursive execution stopped.
${PSTD FAILED} \n\nAlso parent suite teardown failed:\n${LIMIT EXCEEDED}
*** Test Cases ***
diff --git a/atest/testdata/running/duplicate_test_name.robot b/atest/testdata/running/duplicate_test_name.robot
index 503aa78ebc4..0388cee2438 100644
--- a/atest/testdata/running/duplicate_test_name.robot
+++ b/atest/testdata/running/duplicate_test_name.robot
@@ -1,27 +1,58 @@
+*** Variables ***
+${INDEX} ${1}
+
*** Test Cases ***
-Same Test Multiple Times
- No Operation
+Duplicates
+ [Documentation] FAIL Executed!
+ Fail Executed!
-Same Test Multiple Times
- No Operation
+Duplicates
+ [Documentation] FAIL Executed!
+ Fail Executed!
-Same Test Multiple Times
- No Operation
+Duplicates
+ [Documentation] FAIL Executed!
+ Fail Executed!
-Same Test With Different Case And Spaces
- [Documentation] FAIL Expected failure
- Fail Expected failure
+Duplicates with different case and spaces
+ [Documentation] FAIL Executed!
+ Fail Executed!
-SameTestwith Different CASE and s p a c e s
- No Operation
+Duplicates with different CASE ands p a c e s
+ [Documentation] FAIL Executed!
+ Fail Executed!
-Same Test In Data But Only One Executed
+Duplicates but only one executed
[Tags] exclude
- No Operating
+ Fail Not executed!
-Same Test In Data But Only One Executed
- [Tags] exclude
- No Operation
+Duplicates after resolving ${{'variables'}}
+ [Documentation] FAIL Executed!
+ Fail Executed!
+
+${{'Duplicates'}} after resolving variables
+ [Documentation] FAIL Executed!
+ Fail Executed!
+
+Duplicates but only one executed
+ [Tags] robot:exclude
+ Fail Not executed!
+
+Duplicates but only one executed
+ [Documentation] FAIL Executed!
+ Fail Executed!
+
+Test ${INDEX}
+ [Documentation] FAIL Executed!
+ VAR ${INDEX} ${INDEX + 1} scope=SUITE
+ Fail Executed!
+
+Test ${INDEX}
+ [Documentation] FAIL Executed!
+ VAR ${INDEX} ${INDEX + 1} scope=SUITE
+ Fail Executed!
-Same Test In Data But Only One Executed
- Log This is executed!
+Test ${INDEX}
+ [Documentation] FAIL Executed!
+ VAR ${INDEX} ${INDEX + 1} scope=SUITE
+ Fail Executed!
diff --git a/atest/testdata/running/exit_on_failure_tag.robot b/atest/testdata/running/exit_on_failure_tag.robot
new file mode 100644
index 00000000000..547eb971fd2
--- /dev/null
+++ b/atest/testdata/running/exit_on_failure_tag.robot
@@ -0,0 +1,21 @@
+*** Test Cases ***
+Passing test with the tag has not special effect
+ [Tags] robot:exit-on-failure
+ Log Nothing to worry here!
+
+Failing test without the tag has no special effect
+ [Documentation] FAIL Something bad happened!
+ Fail Something bad happened!
+
+Failing test with the tag initiates exit-on-failure
+ [Documentation] FAIL Something worse happened!
+ [Tags] ROBOT:${{'exit'}}-on-failure
+ Fail Something worse happened!
+
+Subsequent tests are not run 1
+ [Documentation] FAIL Test execution stopped due to a fatal error.
+ Fail Not executed.
+
+Subsequent tests are not run 2
+ [Documentation] FAIL Test execution stopped due to a fatal error.
+ Fail Not executed.
diff --git a/atest/testdata/running/expbytevalues.py b/atest/testdata/running/expbytevalues.py
index 3547c2b3a0b..0e94d696f3a 100644
--- a/atest/testdata/running/expbytevalues.py
+++ b/atest/testdata/running/expbytevalues.py
@@ -1,8 +1,10 @@
-VARIABLES = dict(exp_return_value=b'ty\xf6paikka',
- exp_return_msg='ty\\xf6paikka',
- exp_error_msg="b'hyv\\xe4'",
- exp_log_msg="b'\\xe4iti'",
- exp_log_multiline_msg="b'\\xe4iti\\nis\\xe4'")
+VARIABLES = dict(
+ exp_return_value=b"ty\xf6paikka",
+ exp_return_msg="työpaikka",
+ exp_error_msg="b'hyv\\xe4'",
+ exp_log_msg="b'\\xe4iti'",
+ exp_log_multiline_msg="b'\\xe4iti\\nis\\xe4'",
+)
def get_variables():
diff --git a/atest/testdata/running/failures_in_teardown.robot b/atest/testdata/running/failures_in_teardown.robot
index cd678a5793b..f5859997118 100644
--- a/atest/testdata/running/failures_in_teardown.robot
+++ b/atest/testdata/running/failures_in_teardown.robot
@@ -43,7 +43,7 @@ Failure When Setting Variables
No Operation
[Teardown] Failure when setting variables
-Failure In For Loop
+Failure In FOR Loop
[Documentation] FAIL Teardown failed:
... Several failures occurred:
...
@@ -57,7 +57,7 @@ Failure In For Loop
...
... ${SUITE TEARDOWN FAILED}
No Operation
- [Teardown] Failures In For Loop
+ [Teardown] Failures In FOR Loop
Execution Continues After Test Timeout
[Documentation] FAIL Teardown failed:
@@ -76,7 +76,7 @@ Execution Stops After Keyword Timeout
No Operation
[Teardown] Keyword Timeout Occurs
-Execution Continues After Keyword Timeout Occurs In Executed Keyword
+Execution continues if executed keyword fails for keyword timeout
[Documentation] FAIL Teardown failed:
... Several failures occurred:
...
@@ -88,6 +88,14 @@ Execution Continues After Keyword Timeout Occurs In Executed Keyword
No Operation
[Teardown] Keyword Timeout Occurs In Executed Keyword
+Execution stops after keyword timeout if keyword uses WUKS
+ [Documentation] FAIL Teardown failed:
+ ... Keyword timeout 100 milliseconds exceeded.
+ ...
+ ... ${SUITE TEARDOWN FAILED}
+ No Operation
+ [Teardown] Keyword Using WUKS
+
Execution Continues If Variable Does Not Exist
[Documentation] FAIL Teardown failed:
... Several failures occurred:
@@ -150,7 +158,7 @@ Failure when setting variables
${ret} = Fail Return values is None
Should Be Equal ${ret} ${None}
-Failures In For Loop
+Failures In FOR Loop
FOR ${animal} IN cat dog
Fail ${animal}
Fail again
@@ -169,6 +177,16 @@ Keyword Timeout Occurs In Executed Keyword
Keyword Timeout Occurs
Fail This should be executed
+Keyword Using WUKS
+ [Timeout] 100ms
+ Wait Until Keyword Succeeds 4x 1s
+ ... Fail Slowly
+ Fail This should not be executed
+
+Fail Slowly
+ Sleep 0.51ms
+ Fail Failing!
+
Missing Variables
Log ${this var does not exist}
Log This should be executed
diff --git a/atest/testdata/running/flatten.robot b/atest/testdata/running/flatten.robot
index c70b00b1e3d..45e7b5371e4 100644
--- a/atest/testdata/running/flatten.robot
+++ b/atest/testdata/running/flatten.robot
@@ -25,7 +25,7 @@ UK
Nested UK
[Arguments] ${arg}
- [Tags] robot:flatten
+ [Tags] ROBOT:${{'FLATTEN'}}
Log ${arg}
Nest
@@ -35,7 +35,7 @@ Nest
Log not logged
Loops and stuff
- [Tags] robot:flatten
+ [Tags] robot: flatten
FOR ${i} IN RANGE 5
Log inside for ${i}
IF ${i} > 1
diff --git a/atest/testdata/running/for/binary_list.py b/atest/testdata/running/for/binary_list.py
index f47c58fb017..28d482f33e8 100644
--- a/atest/testdata/running/for/binary_list.py
+++ b/atest/testdata/running/for/binary_list.py
@@ -1,2 +1 @@
-LIST__illegal_values = ('illegal:\x00\x08\x0B\x0C\x0E\x1F',
- 'more:\uFFFE\uFFFF')
+LIST__illegal_values = ("illegal:\x00\x08\x0b\x0c\x0e\x1f", "more:\ufffe\uffff")
diff --git a/atest/testdata/running/for/for.robot b/atest/testdata/running/for/for.robot
index ade2f6b21dd..e53cd9fe2dd 100644
--- a/atest/testdata/running/for/for.robot
+++ b/atest/testdata/running/for/for.robot
@@ -22,7 +22,7 @@ Variables in values
Log ${num}
Log Hello from for loop
No Operation
- Run Keyword If ${num} in [2,6] Log Presidential Candidate! WARN
+ Run Keyword If ${num} in [2, 6] Log Presidential Candidate! WARN
END # I can haz comments??!?
Indentation is not required
@@ -330,56 +330,56 @@ Invalid END
END ooops
No loop values
- [Documentation] FAIL FOR loop has no loop values.
+ [Documentation] FAIL FOR loop has no values.
FOR ${var} IN
Fail Not Executed
END
Fail Not Executed
No loop variables
- [Documentation] FAIL FOR loop has no loop variables.
+ [Documentation] FAIL FOR loop has no variables.
FOR IN one two
Fail Not Executed
END
Fail Not Executed
Invalid loop variable 1
- [Documentation] FAIL FOR loop has invalid loop variable 'ooops'.
+ [Documentation] FAIL Invalid FOR loop variable 'ooops'.
FOR ooops IN a b c
Fail Not Executed
END
Fail Not Executed
Invalid loop variable 2
- [Documentation] FAIL FOR loop has invalid loop variable 'ooops'.
+ [Documentation] FAIL Invalid FOR loop variable 'ooops'.
FOR ${var} ooops IN a b c
Fail Not Executed
END
Fail Not Executed
Invalid loop variable 3
- [Documentation] FAIL FOR loop has invalid loop variable '\@{ooops}'.
+ [Documentation] FAIL Invalid FOR loop variable '\@{ooops}'.
FOR @{ooops} IN a b c
Fail Not Executed
END
Fail Not Executed
Invalid loop variable 4
- [Documentation] FAIL FOR loop has invalid loop variable '\&{ooops}'.
+ [Documentation] FAIL Invalid FOR loop variable '\&{ooops}'.
FOR &{ooops} IN a b c
Fail Not Executed
END
Fail Not Executed
Invalid loop variable 5
- [Documentation] FAIL FOR loop has invalid loop variable '$var'.
+ [Documentation] FAIL Invalid FOR loop variable '$var'.
FOR $var IN one two
Fail Not Executed
END
Fail Not Executed
Invalid loop variable 6
- [Documentation] FAIL FOR loop has invalid loop variable '\${not closed'.
+ [Documentation] FAIL Invalid FOR loop variable '\${not closed'.
FOR ${not closed IN one two three
Fail Not Executed
END
@@ -422,7 +422,7 @@ Separator is case- and space-sensitive 4
FOR without any paramenters
[Documentation] FAIL
... Multiple errors:
- ... - FOR loop has no loop variables.
+ ... - FOR loop has no variables.
... - FOR loop has no 'IN' or other valid separator.
FOR
Fail Not Executed
@@ -430,7 +430,7 @@ FOR without any paramenters
Fail Not Executed
Syntax error in nested loop 1
- [Documentation] FAIL FOR loop has invalid loop variable 'y'.
+ [Documentation] FAIL Invalid FOR loop variable 'y'.
FOR ${x} IN ok
FOR y IN nok
Fail Should not be executed
diff --git a/atest/testdata/running/for/for_in_enumerate.robot b/atest/testdata/running/for/for_in_enumerate.robot
index 604a13517eb..b723e919162 100644
--- a/atest/testdata/running/for/for_in_enumerate.robot
+++ b/atest/testdata/running/for/for_in_enumerate.robot
@@ -89,13 +89,13 @@ Wrong number of variables
END
No values
- [Documentation] FAIL FOR loop has no loop values.
+ [Documentation] FAIL FOR loop has no values.
FOR ${index} ${item} IN ENUMERATE
Fail Should not be executed.
END
No values with start
- [Documentation] FAIL FOR loop has no loop values.
+ [Documentation] FAIL FOR loop has no values.
FOR ${index} ${item} IN ENUMERATE start=0
Fail Should not be executed.
END
diff --git a/atest/testdata/running/for/for_in_range.robot b/atest/testdata/running/for/for_in_range.robot
index 750e2778dd7..1703e9484a4 100644
--- a/atest/testdata/running/for/for_in_range.robot
+++ b/atest/testdata/running/for/for_in_range.robot
@@ -90,7 +90,7 @@ Too many arguments
Fail Not executed
No arguments
- [Documentation] FAIL FOR loop has no loop values.
+ [Documentation] FAIL FOR loop has no values.
FOR ${i} IN RANGE
Fail Not executed
END
diff --git a/atest/testdata/running/group/group.robot b/atest/testdata/running/group/group.robot
new file mode 100644
index 00000000000..c7a577150e2
--- /dev/null
+++ b/atest/testdata/running/group/group.robot
@@ -0,0 +1,53 @@
+*** Settings ***
+Suite Setup Keyword
+Suite Teardown Keyword
+
+*** Test Cases ***
+Basics
+ GROUP 1st group
+ Log Inside group
+ Log Still inside
+ END
+ GROUP
+ ... second
+ Log Inside second group
+ END
+ Log After
+
+Failing
+ [Documentation] FAIL Failing inside GROUP!
+ GROUP Fails
+ Fail Failing inside GROUP!
+ Fail Not run
+ END
+ GROUP Not run
+ Fail Not run
+ END
+
+Anonymous
+ GROUP
+ Log Inside unnamed group
+ END
+
+Variable in name
+ GROUP Test is named: ${TEST_NAME}
+ Log ${TEST_NAME}
+ END
+ GROUP ${42}
+ Log Should be 42
+ END
+
+In user keyword
+ Keyword
+
+*** Keywords ***
+Keyword
+ Log Before
+ GROUP First
+ Log low level
+ Log another low level
+ END
+ GROUP Second
+ Log yet another low level
+ END
+ Log After
diff --git a/atest/testdata/running/group/invalid_group.robot b/atest/testdata/running/group/invalid_group.robot
new file mode 100644
index 00000000000..1256dd5bc56
--- /dev/null
+++ b/atest/testdata/running/group/invalid_group.robot
@@ -0,0 +1,38 @@
+*** Test Cases ***
+END missing
+ [Documentation] FAIL GROUP must have closing END.
+ GROUP This is not closed
+ Fail Not run
+
+Empty
+ [Documentation] FAIL GROUP cannot be empty.
+ GROUP This is empty
+ END
+ Log Outside
+
+Multiple parameters
+ [Documentation] FAIL GROUP accepts only one argument as name, got 3 arguments 'Too', 'many' and 'values'.
+ GROUP Too many values
+ Fail Not run
+ END
+ Log Last Keyword
+
+Non-existing variable in name
+ [Documentation] FAIL Variable '\${non_existing_var}' not found.
+ GROUP ${non_existing_var} in name
+ Fail Not run
+ END
+ Log Last Keyword
+
+Invalid data is not reported after failures
+ [Documentation] FAIL Something bad happened!
+ # We probably should validate syntax before even executing the test and report
+ # such failures early. That should then be done also with other control structures.
+ Fail Something bad happened!
+ GROUP ${non_existing_non_executed_variable_is_ok}
+ Fail Not run
+ END
+ GROUP Empty non-executed GROUP is ok
+ END
+ GROUP Even missing END is ok
+ Fail Not run
diff --git a/atest/testdata/running/group/nesting_group.robot b/atest/testdata/running/group/nesting_group.robot
new file mode 100644
index 00000000000..02f08da788a
--- /dev/null
+++ b/atest/testdata/running/group/nesting_group.robot
@@ -0,0 +1,50 @@
+*** Test Cases ***
+Nested
+ GROUP
+ ${var} Set Variable assignment
+ GROUP This Is A Named Group
+ Should Be Equal ${var} assignment
+ END
+ END
+
+With other control structures
+ IF True
+ GROUP Hello
+ VAR ${i} ${0}
+ END
+ GROUP With WHILE
+ WHILE $i < 2
+ GROUP Group1 Inside WHILE (${i})
+ Log ${i}
+ END
+ GROUP Group2 Inside WHILE
+ VAR ${i} ${i + 1}
+ END
+ END
+ IF $i != 2 Fail Shall be logged but NOT RUN
+ END
+ END
+
+In non-executed branch
+ VAR ${var} value
+ IF True
+ GROUP GROUP in IF
+ Should Be Equal ${var} value
+ IF True
+ Log IF in GROUP
+ ELSE
+ GROUP GROUP in ELSE
+ Fail Shall be logged but NOT RUN
+ END
+ END
+ END
+ ELSE IF False
+ GROUP ${non_existing_variable_is_fine_here}
+ Fail Shall be logged but NOT RUN
+ END
+ ELSE
+ # This possibly should be validated earlier so that the whole test would
+ # fail for a syntax error without executing it.
+ GROUP Even empty GROUP is allowed
+ END
+ END
diff --git a/atest/testdata/running/group/templates.robot b/atest/testdata/running/group/templates.robot
new file mode 100644
index 00000000000..0db15db7c21
--- /dev/null
+++ b/atest/testdata/running/group/templates.robot
@@ -0,0 +1,92 @@
+*** Settings ***
+Test Template Run Keyword
+
+*** Test Cases ***
+Pass
+ GROUP 1
+ Log 1.1
+ END
+ GROUP 2
+ Log 2.1
+ Log 2.2
+ END
+
+Pass and fail
+ [Documentation] FAIL 2.1
+ GROUP 1
+ Log 1.1
+ END
+ GROUP 2
+ Fail 2.1
+ Log 2.2
+ END
+ GROUP 3
+ Log 3.1
+ END
+
+Fail multiple times
+ [Documentation] FAIL Several failures occurred:
+ ...
+ ... 1) 1.1
+ ...
+ ... 2) 2.1
+ ...
+ ... 3) 2.3
+ ...
+ ... 4) 4.1
+ GROUP 1
+ Fail 1.1
+ END
+ GROUP 2
+ Fail 2.1
+ Log 2.2
+ Fail 2.3
+ END
+ GROUP 3
+ Log 3.1
+ END
+ GROUP 4
+ Fail 4.1
+ END
+
+Pass and skip
+ GROUP 1
+ Skip 1.1
+ END
+ GROUP 2
+ Log 2.1
+ END
+ GROUP 3
+ Skip 3.1
+ Log 3.2
+ END
+
+Pass, fail and skip
+ [Documentation] FAIL 1.1
+ GROUP 1
+ Fail 1.1
+ Skip 1.2
+ Log 1.3
+ END
+ GROUP 2
+ Skip 2.1
+ END
+ GROUP 3
+ Log 3.1
+ END
+
+Skip all
+ [Documentation] SKIP All iterations skipped.
+ GROUP 1
+ Skip 1.1
+ Skip 1.2
+ END
+ GROUP 2
+ Skip 2.1
+ END
+
+Just one that is skipped
+ [Documentation] SKIP 1.1
+ GROUP 1
+ Skip 1.1
+ END
diff --git a/atest/testdata/running/if/invalid_if.robot b/atest/testdata/running/if/invalid_if.robot
index ad838e4f585..7232a694041 100644
--- a/atest/testdata/running/if/invalid_if.robot
+++ b/atest/testdata/running/if/invalid_if.robot
@@ -69,6 +69,29 @@ Recommend $var syntax if invalid condition contains ${var}
Fail Shouldn't be run
END
+$var recommendation with multiple variables
+ [Documentation] FAIL Invalid IF condition: \
+ ... Evaluating expression "x == 'x' or x == 'y'" failed: NameError: name 'x' is not defined nor importable as module
+ ...
+ ... Variables in the original expression "\${x} == 'x' or \${x} == 'y'" were resolved before the expression was evaluated. \
+ ... Try using "$x == 'x' or $x == 'y'" syntax to avoid that. See Evaluating Expressions appendix in Robot Framework User Guide for more details.
+ VAR ${x} x
+ IF ${x} == 'x' or ${x} == 'y'
+ Fail Shouldn't be run
+ END
+
+Remove quotes around variable in $var recommendation
+ [Documentation] FAIL GLOB: Invalid IF condition: \
+ ... Evaluating expression '\\'\\'x"\\' == \\'x\\' or "\\'x"" == \\'x\\' or """"""y\\'\\'\\'""" == \\'y\\' or \\'\\'\\'"""y\\'\\'\\'\\'\\'\\' == \\'y\\'' failed: SyntaxError: *
+ ...
+ ... Variables in the original expression '\\'\${x}\\' == \\'x\\' or "\${x}" == \\'x\\' or """\${y}""" == \\'y\\' or \\'\\'\\'\${y}\\'\\'\\' == \\'y\\'' were resolved before the expression was evaluated. \
+ ... Try using "$x == 'x' or $x == 'x' or $y == 'y' or $y == 'y'" syntax to avoid that. See Evaluating Expressions appendix in Robot Framework User Guide for more details.
+ VAR ${x} 'x"
+ VAR ${y} """y'''
+ IF '${x}' == 'x' or "${x}" == 'x' or """${y}""" == 'y' or '''${y}''' == 'y'
+ Fail Shouldn't be run
+ END
+
IF without END
[Documentation] FAIL IF must have closing END.
IF ${True}
diff --git a/atest/testdata/running/pass_execution_library.py b/atest/testdata/running/pass_execution_library.py
index b40a2f80492..7e6d39ceb5c 100644
--- a/atest/testdata/running/pass_execution_library.py
+++ b/atest/testdata/running/pass_execution_library.py
@@ -7,4 +7,4 @@ def raise_pass_execution_exception(msg):
def call_pass_execution_method(msg):
- BuiltIn().pass_execution(msg, 'lol')
+ BuiltIn().pass_execution(msg, "lol")
diff --git a/atest/testdata/running/setup_and_teardown_using_embedded_arguments.robot b/atest/testdata/running/setup_and_teardown_using_embedded_arguments.robot
new file mode 100644
index 00000000000..5f78cf9163d
--- /dev/null
+++ b/atest/testdata/running/setup_and_teardown_using_embedded_arguments.robot
@@ -0,0 +1,73 @@
+*** Settings ***
+Suite Setup Embedded "arg"
+Suite Teardown Object ${LIST}
+
+*** Variables ***
+${ARG} arg
+${QUOTED} "${ARG}"
+@{LIST} one ${2}
+${NOT} not, exact match instead
+
+*** Test Cases ***
+Test setup and teardown
+ [Setup] Embedded "arg"
+ No Operation
+ [Teardown] Embedded "arg"
+
+Keyword setup and teardown
+ Keyword setup and teardown
+
+Argument as variable
+ [Setup] Embedded "${ARG}"
+ Keyword setup and teardown as variable
+ [Teardown] Embedded "${ARG}"
+
+Argument as non-string variable
+ [Setup] Object ${LIST}
+ Keyword setup and teardown as non-string variable
+ [Teardown] Object ${LIST}
+
+Argument matching only after replacing variables
+ [Setup] Embedded ${QUOTED}
+ Keyword setup and teardown matching only after replacing variables
+ [Teardown] Embedded ${QUOTED}
+
+Exact match after replacing variables has higher precedence
+ [Setup] Embedded ${NOT}
+ Exact match after replacing variables has higher precedence
+ [Teardown] Embedded ${NOT}
+
+*** Keywords ***
+Embedded "${arg}"
+ Should Be Equal ${arg} arg
+
+Object ${arg}
+ Should Be Equal ${arg} ${LIST}
+
+Keyword setup and teardown
+ [Setup] Embedded "arg"
+ No Operation
+ [Teardown] Embedded "arg"
+
+Keyword setup and teardown as variable
+ [Setup] Embedded "${ARG}"
+ No Operation
+ [Teardown] Embedded "${ARG}"
+
+Keyword setup and teardown as non-string variable
+ [Setup] Object ${LIST}
+ No Operation
+ [Teardown] Object ${LIST}
+
+Keyword setup and teardown matching only after replacing variables
+ [Setup] Embedded ${QUOTED}
+ No Operation
+ [Teardown] Embedded ${QUOTED}
+
+Embedded not, exact match instead
+ No Operation
+
+Exact match after replacing variables has higher precedence
+ [Setup] Embedded ${NOT}
+ No Operation
+ [Teardown] Embedded ${NOT}
diff --git a/atest/testdata/running/skip/all_skipped/tests.robot b/atest/testdata/running/skip/all_skipped/tests.robot
index b973819f7b0..cb0492a9f97 100644
--- a/atest/testdata/running/skip/all_skipped/tests.robot
+++ b/atest/testdata/running/skip/all_skipped/tests.robot
@@ -1,6 +1,6 @@
*** Settings ***
-Suite Setup Fail Because all tests are skipped
-Suite Teardown Fail these should not be run
+Suite Setup Fail Because all tests are skipped or excluded,
+Suite Teardown Fail suite setup and teardown should not be run.
*** Test Cases ***
Skip using robot:skip
@@ -9,7 +9,7 @@ Skip using robot:skip
Fail Should not be run
Skip using --skip
- [Documentation] SKIP Test skipped using '--skip' command line option.
+ [Documentation] SKIP Test skipped using 'skip-this' tag.
[Tags] skip-this
Fail Should not be run
diff --git a/atest/testdata/running/skip/skip.robot b/atest/testdata/running/skip/skip.robot
index fbd48010b37..2d6ddb94c0b 100644
--- a/atest/testdata/running/skip/skip.robot
+++ b/atest/testdata/running/skip/skip.robot
@@ -2,7 +2,8 @@
Library skiplib.py
*** Variables ***
-${TEST_OR_TASK} Test
+${TEST_OR_TASK} test
+${SKIP} skip
*** Test Cases ***
Skip keyword
@@ -180,7 +181,7 @@ Skip with Pass Execution in Teardown
Skip in Teardown with Pass Execution in Body
[Documentation] SKIP Then we skip
Pass Execution First we pass
- [Teardown] Skip Then we skip
+ [Teardown] Skip Then we skip
Skip with Run Keyword and Ignore Error
[Documentation] SKIP Skip from within
@@ -204,70 +205,97 @@ Skip with Wait Until Keyword Succeeds
Fail Should not be executed!
Skipped with --skip
- [Documentation] SKIP ${TEST_OR_TASK} skipped using '--skip' command line option.
+ [Documentation] SKIP ${TEST_OR_TASK.title()} skipped using 'skip-this' tag.
[Tags] skip-this
- Fail
+ Fail Should not be executed!
-Skipped when test is tagged with robot:skip
- [Documentation] SKIP
- ... Test skipped using 'robot:skip' tag.
+Skipped with --skip when tag uses variable
+ [Documentation] SKIP ${TEST_OR_TASK.title()} skipped using 'skip-this' tag.
+ [Tags] ${SKIP}-this
+ Fail Should not be executed!
+
+Skipped with robot:skip
+ [Documentation] SKIP Test skipped using 'robot:skip' tag.
[Tags] robot:skip
- Fail Test should not be executed
+ Fail Should not be executed!
+
+Skipped with robot:skip when tag uses variable
+ [Documentation] SKIP Test skipped using 'robot:skip' tag.
+ [Tags] robot:${SKIP} robot:whatever
+ Fail Should not be executed!
Skipped with --SkipOnFailure
[Documentation] SKIP
- ... ${TEST_OR_TASK} failed but skip-on-failure mode was active and it was marked skipped.
+ ... Failed ${TEST_OR_TASK} skipped using 'skip-on-failure' tag.
...
... Original failure:
... Ooops, we fail!
[Tags] skip-on-failure
Fail Ooops, we fail!
-Skipped with --SkipOnFailure when Failure in Test Setup
+Skipped with --SkipOnFailure when tag uses variable
[Documentation] SKIP
- ... ${TEST_OR_TASK} failed but skip-on-failure mode was active and it was marked skipped.
+ ... Failed ${TEST_OR_TASK} skipped using 'skip-on-failure' tag.
+ ...
+ ... Original failure:
+ ... Ooops, we fail!
+ [Tags] ${SKIP}-on-failure
+ Fail Ooops, we fail!
+
+Skipped with --SkipOnFailure when failure in setup
+ [Documentation] SKIP
+ ... Failed ${TEST_OR_TASK} skipped using 'skip-on-failure' tag.
...
... Original failure:
... Setup failed:
... failure in setup
- [Tags] skip-on-failure
+ [Tags] SKIP-ON-FAILURE
[Setup] Fail failure in setup
No Operation
-Skipped with --SkipOnFailure when Failure in Test Teardown
+Skipped with --SkipOnFailure when failure in teardown
[Documentation] SKIP
- ... ${TEST_OR_TASK} failed but skip-on-failure mode was active and it was marked skipped.
+ ... Failed ${TEST_OR_TASK} skipped using 'skip-on-failure' tag.
...
... Original failure:
... Teardown failed:
... failure in teardown
[Tags] skip-on-failure
- [Teardown] Fail failure in teardown
No Operation
+ [Teardown] Fail failure in teardown
-Skipped with --SkipOnFailure when Set Tags Used in Teardown
+Skipped with --SkipOnFailure when Set Tags used in teardown
[Documentation] SKIP
- ... ${TEST_OR_TASK} failed but skip-on-failure mode was active and it was marked skipped.
+ ... Failed ${TEST_OR_TASK} skipped using 'skip-on-failure' tag.
...
... Original failure:
... Ooops, we fail!
Fail Ooops, we fail!
[Teardown] Set Tags skip-on-failure
-Skipped although test fails since test is tagged with robot:skip-on-failure
+Skipped with robot:skip-on-failure
+ [Documentation] SKIP
+ ... Failed ${TEST_OR_TASK} skipped using 'robot:skip-on-failure' tag.
+ ...
+ ... Original failure:
+ ... We fail here, but the test is reported as skipped.
+ [Tags] robot:skip-on-failure
+ Fail We fail here, but the test is reported as skipped.
+
+Skipped with robot:skip-on-failure when tag uses variable
[Documentation] SKIP
- ... ${TEST_OR_TASK} failed but skip-on-failure mode was active and it was marked skipped.
+ ... Failed ${TEST_OR_TASK} skipped using 'robot:skip-on-failure' tag.
...
... Original failure:
- ... We failed here, but the test is reported as skipped instead
- [Tags] robot:skip-on-failure
- Fail We failed here, but the test is reported as skipped instead
+ ... We fail here, but the test is reported as skipped.
+ [Tags] robot:${SKIP}-on-FAILURE
+ Fail We fail here, but the test is reported as skipped.
-Failing Test
+Failing
[Documentation] FAIL AssertionError
Fail
-Passing Test
+Passing
No Operation
*** Keywords ***
diff --git a/atest/testdata/running/skip_with_template.robot b/atest/testdata/running/skip_with_template.robot
new file mode 100644
index 00000000000..9672d1b0b1e
--- /dev/null
+++ b/atest/testdata/running/skip_with_template.robot
@@ -0,0 +1,97 @@
+*** Settings ***
+Library AddMessagesToTestBody name=Messages in test body are ignored
+Test Template Run Keyword
+
+*** Test Cases ***
+SKIP + PASS -> PASS
+ Skip Skipped
+ Log Passed
+
+FAIL + ANY -> FAIL
+ [Documentation] FAIL Failed
+ Log Passed
+ Skip Skipped
+ Log Passed
+ Fail Failed
+ Skip Skipped
+
+Only SKIP -> SKIP
+ [Documentation] SKIP All iterations skipped.
+ Skip Skipped
+ Skip Skipped
+
+IF w/ SKIP + PASS -> PASS
+ IF True
+ Skip Skipped
+ Log Passed
+ ELSE
+ Fail Not executed
+ END
+ IF True Skip Skipped
+ IF True Log Passed
+
+IF w/ FAIL + ANY -> FAIL
+ [Documentation] FAIL Failed
+ IF False
+ Fail Not executed
+ ELSE
+ Log Passed
+ Skip Skipped
+ Log Passed
+ Fail Failed
+ Skip Skipped
+ END
+ IF True Skip Skipped
+ IF True Log Passed
+
+IF w/ only SKIP -> SKIP
+ [Documentation] SKIP All iterations skipped.
+ IF True
+ Skip Skip 1
+ Skip Skip 2
+ END
+ IF True Skip Skip 3
+ IF True Skip Skip 4
+
+FOR w/ SKIP + PASS -> PASS
+ FOR ${x} IN a b
+ Skip ${x}
+ Log ${x}
+ END
+ FOR ${x} IN just once
+ Skip ${x}
+ END
+ FOR ${x} IN just once
+ Skip ${x}
+ Log ${x}
+ END
+
+FOR w/ FAIL + ANY -> FAIL
+ [Documentation] FAIL Several failures occurred:\n\n1) a\n\n2) b
+ FOR ${x} IN a b
+ Skip ${x}
+ Fail ${x}
+ Log ${x}
+ END
+ FOR ${x} IN just once
+ Skip ${x}
+ END
+ FOR ${x} IN just once
+ Skip ${x}
+ Log ${x}
+ END
+
+FOR w/ only SKIP -> SKIP
+ [Documentation] SKIP All iterations skipped.
+ FOR ${x} IN a b
+ Skip ${x} 1
+ Skip ${x} 2
+ END
+ FOR ${x} IN just once
+ Skip ${x}
+ END
+
+Messages in test body are ignored
+ Log Library listener adds messages to body of this test.
+ Skip If True This iteration is skipped!
+ Log This iteration passes!
diff --git a/atest/testdata/running/steps_after_failure.robot b/atest/testdata/running/steps_after_failure.robot
index 71cd50650c2..5cbe7360215 100644
--- a/atest/testdata/running/steps_after_failure.robot
+++ b/atest/testdata/running/steps_after_failure.robot
@@ -42,6 +42,14 @@ IF after failure
${x} = Fail This should not be run
END
+GROUP after failure
+ [Documentation] FAIL This fails
+ Fail This fails
+ GROUP Group Name
+ Fail This should not be run
+ ${x} = Fail This should not be run
+ END
+
FOR after failure
[Documentation] FAIL This fails
Fail This fails
@@ -103,8 +111,10 @@ Nested control structure after failure
IF True
FOR ${y} IN RANGE ${x}
Fail This should not be run
- Fail This should not be run
- Fail This should not be run
+ GROUP This should not be run
+ Fail This should not be run
+ Fail This should not be run
+ END
END
Fail This should not be run
ELSE
@@ -159,6 +169,20 @@ Failure in ELSE branch
END
Fail This should not be run
+Failure in GROUP
+ [Documentation] FAIL This fails
+ GROUP Group Name 0
+ GROUP Group Name 0,0
+ Fail This fails
+ Fail This should not be run
+ END
+ Fail This should not be run
+ GROUP Group Name 0,1
+ Fail This should not be run
+ END
+ END
+ Fail This should not be run
+
Failure in FOR iteration
[Documentation] FAIL This fails
FOR ${x} IN RANGE 100
diff --git a/atest/testdata/running/stopping_with_signal/Library.py b/atest/testdata/running/stopping_with_signal/Library.py
index 2dba2be3aac..cec00c644e4 100755
--- a/atest/testdata/running/stopping_with_signal/Library.py
+++ b/atest/testdata/running/stopping_with_signal/Library.py
@@ -10,7 +10,7 @@ def busy_sleep(seconds):
def swallow_exception(timeout=3):
try:
busy_sleep(timeout)
- except:
+ except Exception:
pass
else:
- raise AssertionError('Expected exception did not occur!')
+ raise AssertionError("Expected exception did not occur!")
diff --git a/atest/testdata/running/stopping_with_signal/test_signalhandler_is_reset.py b/atest/testdata/running/stopping_with_signal/test_signalhandler_is_reset.py
new file mode 100644
index 00000000000..6309766cd55
--- /dev/null
+++ b/atest/testdata/running/stopping_with_signal/test_signalhandler_is_reset.py
@@ -0,0 +1,18 @@
+import signal
+
+from robot.api import TestSuite
+
+suite = TestSuite.from_string("""
+*** Test Cases ***
+Test
+ Sleep ${DELAY}
+""").config(name="Suite") # fmt: skip
+
+signal.signal(signal.SIGALRM, lambda signum, frame: signal.raise_signal(signal.SIGINT))
+signal.setitimer(signal.ITIMER_REAL, 1)
+
+result = suite.run(variable="DELAY:5", output=None, log=None, report=None)
+assert result.suite.elapsed_time.total_seconds() < 1.5
+assert result.suite.status == "FAIL"
+result = suite.run(variable="DELAY:0", output=None, log=None, report=None)
+assert result.suite.status == "PASS"
diff --git a/atest/testdata/running/test_template.robot b/atest/testdata/running/test_template.robot
index 92c2529706c..46d8ed1f4da 100644
--- a/atest/testdata/running/test_template.robot
+++ b/atest/testdata/running/test_template.robot
@@ -158,7 +158,7 @@ Nested FOR
Invalid FOR
[Documentation] FAIL
... Multiple errors:
- ... - FOR loop has no loop values.
+ ... - FOR loop has no values.
... - FOR loop must have closing END.
FOR ${x} IN
${x} not run
@@ -338,7 +338,7 @@ Templated test continues after non-syntax errors
...
... 1) Variable '\${this does not exist}' not found.
...
- ... 2) Keyword 'BuiltIn.Should Be Equal' expected 2 to 8 arguments, got 1.
+ ... 2) Keyword 'BuiltIn.Should Be Equal' expected 2 to 10 arguments, got 1.
...
... 3) Compared and not equal != Fails
${this does not exist} ${this does not exist either}
diff --git a/atest/testdata/running/timeouts.robot b/atest/testdata/running/timeouts.robot
index ffa33a74d99..660c0043795 100644
--- a/atest/testdata/running/timeouts.robot
+++ b/atest/testdata/running/timeouts.robot
@@ -317,7 +317,7 @@ Timeouted UK Using Timeouted UK
Run Keyword With Timeout
[Timeout] 200 milliseconds
- Run Keyword Unless False Log Hello
+ Run Keyword Log Hello
Run Keyword If True Sleep 3
Keyword timeout from variable
diff --git a/atest/testdata/running/timeouts_with_logging.py b/atest/testdata/running/timeouts_with_logging.py
index 880fb89f25d..c0c58f1ab6d 100644
--- a/atest/testdata/running/timeouts_with_logging.py
+++ b/atest/testdata/running/timeouts_with_logging.py
@@ -4,21 +4,19 @@
from robot.api import logger
from robot.output.pyloggingconf import RobotHandler
-
# Use simpler formatter to avoid flakeynes that started to occur after enhancing
# message formatting in https://github.com/robotframework/robotframework/pull/4147
# Without this change execution on PyPy failed about every third time so that
# timeout was somehow ignored. On CI the problem occurred also with Python 3.9.
-# Not sure why the problem occurred but it seems to be related to the logging
+# Not sure why the problem occurred, but it seems to be related to the logging
# module and not related to the bug that this library is testing. This hack ought
-# ought to thus be safe. With it was able to run tests locally 100 times using
-# PyPy without problems.
+# to thus be safe.
for handler in logging.getLogger().handlers:
if isinstance(handler, RobotHandler):
handler.format = lambda record: record.getMessage()
-MSG = 'A rather long message that is slow to write on the disk. ' * 10000
+MSG = "A rather long message that is slow to write on the disk. " * 10000
def rf_logger():
@@ -29,14 +27,11 @@ def python_logger():
_log_a_lot(logging.info)
-def _log_a_lot(info):
- # Assigning local variables is performance optimization to give as much
- # time as as possible for actual logging.
- msg = MSG
- sleep = time.sleep
- current = time.time
+# Binding global values to argument default values is a performance optimization
+# to give as much time as possible for actual logging.
+def _log_a_lot(info, msg=MSG, sleep=time.sleep, current=time.time):
end = current() + 1
while current() < end:
info(msg)
- sleep(0) # give time for other threads
- raise AssertionError('Execution should have been stopped by timeout.')
+ sleep(0) # give time for other threads
+ raise AssertionError("Execution should have been stopped by timeout.")
diff --git a/atest/testdata/running/try_except/invalid_try_except.robot b/atest/testdata/running/try_except/invalid_try_except.robot
index 0a117274627..ca50eb073bf 100644
--- a/atest/testdata/running/try_except/invalid_try_except.robot
+++ b/atest/testdata/running/try_except/invalid_try_except.robot
@@ -205,7 +205,7 @@ FINALLY before ELSE
END
Template with TRY
- [Documentation] FAIL Templates cannot be used with TRY.
+ [Documentation] FAIL TRY does not support templates.
[Template] Log many
TRY
Fail Should not be executed
@@ -214,7 +214,7 @@ Template with TRY
END
Template with TRY inside IF
- [Documentation] FAIL Templates cannot be used with TRY.
+ [Documentation] FAIL TRY does not support templates.
[Template] Log many
IF True
TRY
@@ -228,7 +228,7 @@ Template with IF inside TRY
[Documentation] FAIL
... Multiple errors:
... - TRY must have closing END.
- ... - Templates cannot be used with TRY.
+ ... - TRY does not support templates.
[Template] Log many
TRY
IF True
diff --git a/atest/testdata/running/while/invalid_while.robot b/atest/testdata/running/while/invalid_while.robot
index c9a2b7bc129..c1c1e57638d 100644
--- a/atest/testdata/running/while/invalid_while.robot
+++ b/atest/testdata/running/while/invalid_while.robot
@@ -88,3 +88,11 @@ Non-existing variable in condition causes normal error
EXCEPT Invalid WHILE loop condition: Evaluating expression '\${bad}' failed: Variable '\${bad}' not found.
No Operation
END
+
+Templatest are not supported
+ [Documentation] FAIL WHILE does not support templates.
+ [Template] Log
+ WHILE True
+ 1
+ 2
+ END
diff --git a/atest/testdata/running/while/on_limit.robot b/atest/testdata/running/while/on_limit.robot
index 6b1cc25348e..b8af65b5b4c 100644
--- a/atest/testdata/running/while/on_limit.robot
+++ b/atest/testdata/running/while/on_limit.robot
@@ -67,41 +67,35 @@ Invalid on_limit
END
Invalid on_limit from variable
- [Documentation] FAIL Invalid WHILE loop 'on_limit' value: Value 'inValid' is not accepted. Valid values are 'PASS' and 'FAIL'.
+ [Documentation] FAIL Invalid WHILE loop 'on_limit': Value 'inValid' is not accepted. Valid values are 'PASS' and 'FAIL'.
WHILE True limit=5 on_limit=${{'inValid'}}
Fail Should not be executed
END
-On limit without limit defined
+On limit without limit
[Documentation] FAIL WHILE option 'on_limit' cannot be used without 'limit'.
WHILE True on_limit=PaSS
Fail Should not be executed
END
On limit with invalid variable
- [Documentation] FAIL Invalid WHILE loop 'on_limit' value: Variable '\${does not exist}' not found.
+ [Documentation] FAIL Invalid WHILE loop 'on_limit': Variable '\${does not exist}' not found.
WHILE True limit=5 on_limit=${does not exist}
Fail Should not be executed
END
-On limit message without limit
- [Documentation] FAIL Error
- WHILE $variable < 2 on_limit_message=Error
- Log ${variable}
- END
-
-Wrong WHILE argument
- [Documentation] FAIL WHILE accepts only one condition, got 3 conditions '$variable < 2', 'limit=5' and 'limit_exceed_messag=Custom error message'.
- WHILE $variable < 2 limit=5 limit_exceed_messag=Custom error message
- Fail Should not be executed
- END
-
On limit message
[Documentation] FAIL Custom error message
WHILE $variable < 2 limit=${limit} on_limit_message=Custom error message
Log ${variable}
END
+On limit message without limit
+ [Documentation] FAIL Error
+ WHILE True on_limit_message=Error
+ No Operation
+ END
+
On limit message from variable
[Documentation] FAIL ${errorMsg}
WHILE $variable < 2 limit=5 on_limit_message=${errorMsg}
@@ -114,7 +108,7 @@ Part of on limit message from variable
Log ${variable}
END
-No on limit message
+On limit message is not used if limit is not hit
WHILE $variable < 3 limit=10 on_limit_message=${errorMsg} 2
Log ${variable}
${variable}= Evaluate $variable + 1
@@ -136,6 +130,6 @@ On limit message before limit
On limit message with invalid variable
[Documentation] FAIL Invalid WHILE loop 'on_limit_message': 'Variable '${nonExisting}' not found.
- WHILE $variable < 2 on_limit_message=${nonExisting} limit=5
+ WHILE $variable < 2 on_limit_message=${nonExisting}
Fail Should not be executed
END
diff --git a/atest/testdata/running/while/while_limit.robot b/atest/testdata/running/while/while_limit.robot
index 5e07d90a4e6..ad41c9db64c 100644
--- a/atest/testdata/running/while/while_limit.robot
+++ b/atest/testdata/running/while/while_limit.robot
@@ -7,8 +7,8 @@ ${USE LIMIT} Use the 'limit' argument to increase or remove the limit if neede
*** Test Cases ***
Default limit is 10000 iterations
[Documentation] FAIL WHILE loop was aborted because it did not finish within the limit of 10000 iterations. ${USE LIMIT}
- WHILE $variable < 2
- Log ${variable}
+ WHILE True
+ No Operation
END
Limit with iteration count
@@ -57,8 +57,7 @@ Part of limit from variable
END
Limit can be disabled
- WHILE $variable < 110 limit=NoNe
- Log ${variable}
+ WHILE $variable < 10042 limit=NoNe
${variable}= Evaluate $variable + 1
END
@@ -114,6 +113,12 @@ Invalid limit mistyped prefix
Fail Should not be executed
END
+Limit with non-existing variable
+ [Documentation] FAIL Invalid WHILE loop limit: Variable '\${bad}' not found.
+ WHILE limit=${bad}
+ Fail Should not be executed
+ END
+
Limit used multiple times
[Documentation] FAIL WHILE accepts only one condition, got 2 conditions 'True' and 'limit=1'.
WHILE True limit=1 limit=2
diff --git a/atest/testdata/standard_libraries/builtin/DynamicRegisteredLibrary.py b/atest/testdata/standard_libraries/builtin/DynamicRegisteredLibrary.py
index 30a340fdecc..07f3423d3d2 100644
--- a/atest/testdata/standard_libraries/builtin/DynamicRegisteredLibrary.py
+++ b/atest/testdata/standard_libraries/builtin/DynamicRegisteredLibrary.py
@@ -4,7 +4,7 @@
class DynamicRegisteredLibrary:
def get_keyword_names(self):
- return ['dynamic_run_keyword']
+ return ["dynamic_run_keyword"]
def run_keyword(self, name, args):
dynamic_run_keyword(*args)
@@ -14,5 +14,6 @@ def dynamic_run_keyword(name, *args):
return BuiltIn().run_keyword(name, *args)
-register_run_keyword('DynamicRegisteredLibrary', 'dynamic_run_keyword', 1,
- deprecation_warning=False)
+register_run_keyword(
+ "DynamicRegisteredLibrary", "dynamic_run_keyword", 1, deprecation_warning=False
+)
diff --git a/atest/testdata/standard_libraries/builtin/FailUntilSucceeds.py b/atest/testdata/standard_libraries/builtin/FailUntilSucceeds.py
index 6a661407fe3..15799f77943 100644
--- a/atest/testdata/standard_libraries/builtin/FailUntilSucceeds.py
+++ b/atest/testdata/standard_libraries/builtin/FailUntilSucceeds.py
@@ -2,7 +2,7 @@
class FailUntilSucceeds:
- ROBOT_LIBRARY_SCOPE = 'TESTCASE'
+ ROBOT_LIBRARY_SCOPE = "TESTCASE"
def __init__(self, times_to_fail=0):
self.times_to_fail = int(times_to_fail)
@@ -14,5 +14,5 @@ def fail_until_retried_often_enough(self, message="Hello", sleep=0):
self.times_to_fail -= 1
time.sleep(sleep)
if self.times_to_fail >= 0:
- raise Exception('Still %d times to fail!' % self.times_to_fail)
+ raise Exception(f"Still {self.times_to_fail} times to fail!")
return message
diff --git a/atest/testdata/standard_libraries/builtin/NotRegisteringLibrary.py b/atest/testdata/standard_libraries/builtin/NotRegisteringLibrary.py
index 828f8e11013..a82ac710280 100644
--- a/atest/testdata/standard_libraries/builtin/NotRegisteringLibrary.py
+++ b/atest/testdata/standard_libraries/builtin/NotRegisteringLibrary.py
@@ -2,4 +2,4 @@
def my_run_keyword(name, *args):
- return BuiltIn().run_keyword(name, *args)
\ No newline at end of file
+ return BuiltIn().run_keyword(name, *args)
diff --git a/atest/testdata/standard_libraries/builtin/RegisteredClass.py b/atest/testdata/standard_libraries/builtin/RegisteredClass.py
index ac95ec27b62..457b9cb5b1d 100644
--- a/atest/testdata/standard_libraries/builtin/RegisteredClass.py
+++ b/atest/testdata/standard_libraries/builtin/RegisteredClass.py
@@ -9,7 +9,9 @@ def run_keyword_method(self, name, *args):
return BuiltIn().run_keyword(name, *args)
-register_run_keyword("RegisteredClass", "Run Keyword If Method", 2,
- deprecation_warning=False)
-register_run_keyword("RegisteredClass", "run_keyword_method", 1,
- deprecation_warning=False)
+register_run_keyword(
+ "RegisteredClass", "Run Keyword If Method", 2, deprecation_warning=False
+)
+register_run_keyword(
+ "RegisteredClass", "run_keyword_method", 1, deprecation_warning=False
+)
diff --git a/atest/testdata/standard_libraries/builtin/RegisteringLibrary.py b/atest/testdata/standard_libraries/builtin/RegisteringLibrary.py
index 13e34fcbdfd..5e3a2467426 100644
--- a/atest/testdata/standard_libraries/builtin/RegisteringLibrary.py
+++ b/atest/testdata/standard_libraries/builtin/RegisteringLibrary.py
@@ -6,10 +6,10 @@ def run_keyword_function(name, *args):
def run_keyword_without_keyword(*args):
- return BuiltIn().run_keyword(r'\\Log Many', *args)
+ return BuiltIn().run_keyword(r"\\Log Many", *args)
-register_run_keyword(__name__, 'run_keyword_function', 1,
- deprecation_warning=False)
-register_run_keyword(__name__, 'run_keyword_without_keyword', 0,
- deprecation_warning=False)
+register_run_keyword(__name__, "run_keyword_function", 1, deprecation_warning=False)
+register_run_keyword(
+ __name__, "run_keyword_without_keyword", 0, deprecation_warning=False
+)
diff --git a/atest/testdata/standard_libraries/builtin/UseBuiltIn.py b/atest/testdata/standard_libraries/builtin/UseBuiltIn.py
index 311e7933907..33a662e2801 100644
--- a/atest/testdata/standard_libraries/builtin/UseBuiltIn.py
+++ b/atest/testdata/standard_libraries/builtin/UseBuiltIn.py
@@ -1,24 +1,55 @@
+import time
+
+from robot.api import logger
from robot.libraries.BuiltIn import BuiltIn
+MSG = "A rather long message that is slow to write on the disk. " * 10000
+
-def log_debug_message():
+def log_messages_and_set_log_level():
b = BuiltIn()
- b.set_log_level('DEBUG')
- b.log('Hello, debug world!', 'DEBUG')
+ b.log("Should not be logged because current level is INFO.", "DEBUG")
+ b.set_log_level("NONE")
+ b.log("Not logged!", "WARN")
+ b.set_log_level("DEBUG")
+ b.log("Hello, debug world!", "DEBUG")
def get_test_name():
- return BuiltIn().get_variables()['${TEST NAME}']
+ return BuiltIn().get_variables()["${TEST NAME}"]
def set_secret_variable():
- BuiltIn().set_test_variable('${SECRET}', '*****')
+ BuiltIn().set_test_variable("${SECRET}", "*****")
-def use_run_keyword_with_non_unicode_values():
- BuiltIn().run_keyword('Log', 42)
- BuiltIn().run_keyword('Log', b'\xff')
+def use_run_keyword_with_non_string_values():
+ BuiltIn().run_keyword("Log", 42)
+ BuiltIn().run_keyword("Log", b"\xff")
def user_keyword_via_run_keyword():
- BuiltIn().run_keyword("UseBuiltInResource.Keyword", 'This is x', 911)
+ logger.info("Before")
+ BuiltIn().run_keyword("UseBuiltInResource.Keyword", "This is x", 911)
+ logger.info("After")
+
+
+def recursive_run_keyword(limit: int, round: int = 1):
+ if round <= limit:
+ BuiltIn().run_keyword("Log", round)
+ BuiltIn().run_keyword("Recursive Run Keyword", limit, round + 1)
+
+
+def run_keyword_that_logs_huge_message_until_timeout():
+ while True:
+ BuiltIn().run_keyword("Log Huge Message")
+
+
+def log_huge_message():
+ logger.info(MSG)
+
+
+def timeout_in_parent_keyword_after_running_keyword():
+ BuiltIn().run_keyword("Log", "Hello!")
+ while True:
+ time.sleep(0)
diff --git a/atest/testdata/standard_libraries/builtin/UseBuiltInResource.robot b/atest/testdata/standard_libraries/builtin/UseBuiltInResource.robot
index d5f865d3367..5670a0064d7 100644
--- a/atest/testdata/standard_libraries/builtin/UseBuiltInResource.robot
+++ b/atest/testdata/standard_libraries/builtin/UseBuiltInResource.robot
@@ -1,4 +1,5 @@
*** Keywords ***
Keyword
[Arguments] ${x} ${y} ${z}=zzz
+ [Timeout] 1 hour
Log ${x}-${y}-${z}
diff --git a/atest/testdata/standard_libraries/builtin/broken_containers.py b/atest/testdata/standard_libraries/builtin/broken_containers.py
index 2f808768dc4..7560633f9ad 100644
--- a/atest/testdata/standard_libraries/builtin/broken_containers.py
+++ b/atest/testdata/standard_libraries/builtin/broken_containers.py
@@ -1,16 +1,16 @@
try:
- from collections.abc import Sequence, Mapping
+ from collections.abc import Mapping, Sequence
except ImportError:
- from collections import Sequence, Mapping
+ from collections import Mapping, Sequence
-__all__ = ['BROKEN_ITERABLE', 'BROKEN_SEQUENCE', 'BROKEN_MAPPING']
+__all__ = ["BROKEN_ITERABLE", "BROKEN_SEQUENCE", "BROKEN_MAPPING"]
class BrokenIterable:
def __iter__(self):
- yield 'x'
+ yield "x"
raise ValueError(type(self).__name__)
def __getitem__(self, item):
@@ -28,7 +28,6 @@ class BrokenMapping(BrokenIterable, Mapping):
pass
-
BROKEN_ITERABLE = BrokenIterable()
BROKEN_SEQUENCE = BrokenSequence()
BROKEN_MAPPING = BrokenMapping()
diff --git a/atest/testdata/standard_libraries/builtin/documentation/set_documentation.robot b/atest/testdata/standard_libraries/builtin/documentation/set_documentation.robot
index 24a153ea9b7..98ad3f87e47 100644
--- a/atest/testdata/standard_libraries/builtin/documentation/set_documentation.robot
+++ b/atest/testdata/standard_libraries/builtin/documentation/set_documentation.robot
@@ -17,6 +17,12 @@ Append to test documentation
Should be equal ${TEST DOCUMENTATION} Original doc is continued
Set test documentation \n\ntwice! append=yep
Should be equal ${TEST DOCUMENTATION} Original doc is continued \n\ntwice!
+ Set test documentation thrice append=yes separator=${SPACE}
+ Should be equal ${TEST DOCUMENTATION} Original doc is continued \n\ntwice! thrice
+ Set test documentation ${EMPTY} append=yes separator=!
+ Should be equal ${TEST DOCUMENTATION} Original doc is continued \n\ntwice! thrice!
+ Set test documentation ! append=yes separator=${EMPTY}
+ Should be equal ${TEST DOCUMENTATION} Original doc is continued \n\ntwice! thrice!!
Set suite documentation
Set suite documentation New suite doc
@@ -33,7 +39,11 @@ Append to suite documentation 2
Should be equal ${SUITE DOCUMENTATION} New suite doc is continued
Set suite documentation \n\ntwice! append=yep
Should be equal ${SUITE DOCUMENTATION} New suite doc is continued \n\ntwice!
+ Set suite documentation thrice append=yep separator=,
+ Should be equal ${SUITE DOCUMENTATION} New suite doc is continued \n\ntwice!,thrice
+ Set suite documentation ${1} append=yes separator=?
+ Should be equal ${SUITE DOCUMENTATION} New suite doc is continued \n\ntwice!,thrice?1
Set top level suite documentation
Set suite documentation Appended in test. append=yes top=please
- Should be equal ${SUITE DOCUMENTATION} New suite doc is continued \n\ntwice!
+ Should be equal ${SUITE DOCUMENTATION} New suite doc is continued \n\ntwice!,thrice?1
diff --git a/atest/testdata/standard_libraries/builtin/embedded_args.py b/atest/testdata/standard_libraries/builtin/embedded_args.py
index 1cacf6fd422..3660794cab4 100644
--- a/atest/testdata/standard_libraries/builtin/embedded_args.py
+++ b/atest/testdata/standard_libraries/builtin/embedded_args.py
@@ -9,5 +9,10 @@ def embedded(arg):
@keyword('Embedded object "${obj}" in library')
def embedded_object(obj):
print(obj)
- if obj.name != 'Robot':
+ if obj.name != "Robot":
raise AssertionError(f"'{obj.name}' != 'Robot'")
+
+
+@keyword('Embedded "not" in library')
+def embedded_not():
+ print("Nothing embedded in this library keyword!")
diff --git a/atest/testdata/standard_libraries/builtin/evaluate.robot b/atest/testdata/standard_libraries/builtin/evaluate.robot
index 769bd9d0e5a..5eaedda326d 100644
--- a/atest/testdata/standard_libraries/builtin/evaluate.robot
+++ b/atest/testdata/standard_libraries/builtin/evaluate.robot
@@ -27,6 +27,10 @@ Evaluate
Should Be Equal ${stat} ${True}
Evaluate INVALID
+Custom additions to builtins are supported
+ ${foo} = Evaluate setattr(builtins, 'foo', 'bar') or foo
+ Should Be Equal ${foo} bar
+
Modules are imported automatically
${ceil} = Evaluate math.ceil(1.001)
Should Be Equal ${ceil} ${2}
diff --git a/atest/testdata/standard_libraries/builtin/invalidmod.py b/atest/testdata/standard_libraries/builtin/invalidmod.py
index 6b24a115969..bf6368f9f47 100644
--- a/atest/testdata/standard_libraries/builtin/invalidmod.py
+++ b/atest/testdata/standard_libraries/builtin/invalidmod.py
@@ -1 +1 @@
-raise TypeError('This module cannot be imported!')
+raise TypeError("This module cannot be imported!")
diff --git a/atest/testdata/standard_libraries/builtin/length_variables.py b/atest/testdata/standard_libraries/builtin/length_variables.py
index 66c120d437d..956dd15078a 100644
--- a/atest/testdata/standard_libraries/builtin/length_variables.py
+++ b/atest/testdata/standard_libraries/builtin/length_variables.py
@@ -1,7 +1,7 @@
class CustomLen:
def __init__(self, length):
- self._length=length
+ self._length = length
def __len__(self):
return self._length
@@ -13,7 +13,7 @@ def length(self):
return 40
def __str__(self):
- return 'length()'
+ return "length()"
class SizeMethod:
@@ -22,14 +22,14 @@ def size(self):
return 41
def __str__(self):
- return 'size()'
+ return "size()"
class LengthAttribute:
- length=42
+ length = 42
def __str__(self):
- return 'length'
+ return "length"
def get_variables():
@@ -40,5 +40,5 @@ def get_variables():
CUSTOM_LEN_3=CustomLen(3),
LENGTH_METHOD=LengthMethod(),
SIZE_METHOD=SizeMethod(),
- LENGTH_ATTRIBUTE=LengthAttribute()
+ LENGTH_ATTRIBUTE=LengthAttribute(),
)
diff --git a/atest/testdata/standard_libraries/builtin/log.robot b/atest/testdata/standard_libraries/builtin/log.robot
index 18af0eaee1c..4ab65493bb9 100644
--- a/atest/testdata/standard_libraries/builtin/log.robot
+++ b/atest/testdata/standard_libraries/builtin/log.robot
@@ -125,7 +125,7 @@ formatter=type
Log ${now} formatter=type
formatter=invalid
- [Documentation] FAIL ValueError: Invalid formatter 'invalid'. Available 'str', 'repr', 'ascii', 'len', and 'type'.
+ [Documentation] FAIL ValueError: Invalid formatter 'invalid'. Available 'str', 'repr', 'ascii', 'len' and 'type'.
Log x formatter=invalid
Log callable
@@ -139,6 +139,7 @@ Log Many
Log Many @{LIST}
Log Many
Log Many @{EMPTY}
+ Log Many preserve ${EMPTY} empty ${EMPTY}
Log Many -${EMPTY}- -@{EMPTY}- -&{EMPTY}-
Log Many ${LIST}[0] ${DICT}[b]
diff --git a/atest/testdata/standard_libraries/builtin/numbers_to_convert.py b/atest/testdata/standard_libraries/builtin/numbers_to_convert.py
index c7cde6cf532..dad7e6cd497 100644
--- a/atest/testdata/standard_libraries/builtin/numbers_to_convert.py
+++ b/atest/testdata/standard_libraries/builtin/numbers_to_convert.py
@@ -7,9 +7,8 @@ def __int__(self):
return 42 // self.value
def __str__(self):
- return 'MyObject'
+ return "MyObject"
def get_variables():
- return {'object': MyObject(1),
- 'object_failing': MyObject(0)}
+ return {"object": MyObject(1), "object_failing": MyObject(0)}
diff --git a/atest/testdata/standard_libraries/builtin/objects_for_call_method.py b/atest/testdata/standard_libraries/builtin/objects_for_call_method.py
index 46cc0a56239..2ac2c1f868b 100644
--- a/atest/testdata/standard_libraries/builtin/objects_for_call_method.py
+++ b/atest/testdata/standard_libraries/builtin/objects_for_call_method.py
@@ -4,16 +4,16 @@ def __init__(self):
self.args = None
def my_method(self, *args):
- if args == ('FAIL!',):
- raise RuntimeError('Expected failure')
+ if args == ("FAIL!",):
+ raise RuntimeError("Expected failure")
self.args = args
- def kwargs(self, arg1, arg2='default', **kwargs):
- kwargs = ['%s: %s' % item for item in sorted(kwargs.items())]
- return ', '.join([arg1, arg2] + kwargs)
+ def kwargs(self, arg1, arg2="default", **kwargs):
+ kwargs = [f"{k}: {kwargs[k]}" for k in sorted(kwargs)]
+ return ", ".join([arg1, arg2] + kwargs)
def __str__(self):
- return 'String presentation of MyObject'
+ return "String presentation of MyObject"
obj = MyObject()
diff --git a/atest/testdata/standard_libraries/builtin/reload_library/Reloadable.py b/atest/testdata/standard_libraries/builtin/reload_library/Reloadable.py
index 86b20a8a8cd..3d59a8a41b4 100644
--- a/atest/testdata/standard_libraries/builtin/reload_library/Reloadable.py
+++ b/atest/testdata/standard_libraries/builtin/reload_library/Reloadable.py
@@ -1,14 +1,19 @@
-from robot.utils import NormalizedDict
from robot.libraries.BuiltIn import BuiltIn
+from robot.utils import NormalizedDict
BUILTIN = BuiltIn()
-KEYWORDS = NormalizedDict({'add_keyword': ('name', '*args'),
- 'remove_keyword': ('name',),
- 'reload_self': (),
- 'original 1': ('arg',),
- 'original 2': ('arg',),
- 'original 3': ('arg',)})
+KEYWORDS = NormalizedDict(
+ {
+ "add_keyword": ("name", "*args"),
+ "remove_keyword": ("name",),
+ "reload_self": (),
+ "original 1": ("arg",),
+ "original 2": ("arg",),
+ "original 3": ("arg",),
+ }
+)
+
class Reloadable:
@@ -19,16 +24,16 @@ def get_keyword_arguments(self, name):
return KEYWORDS[name]
def get_keyword_documentation(self, name):
- return 'Doc for %s with args %s' % (name, ', '.join(KEYWORDS[name]))
+ args = ", ".join(KEYWORDS[name])
+ return f"Doc for {name} with args {args}"
def run_keyword(self, name, args):
- print("Running keyword '%s' with arguments %s." % (name, args))
+ print(f"Running keyword '{name}' with arguments {args}.")
assert name in KEYWORDS
- if name == 'add_keyword':
+ if name == "add_keyword":
KEYWORDS[args[0]] = args[1:]
- elif name == 'remove_keyword':
+ elif name == "remove_keyword":
KEYWORDS.pop(args[0])
- elif name == 'reload_self':
+ elif name == "reload_self":
BUILTIN.reload_library(self)
return name
-
diff --git a/atest/testdata/standard_libraries/builtin/reload_library/StaticLibrary.py b/atest/testdata/standard_libraries/builtin/reload_library/StaticLibrary.py
index 88d9904a8cc..b4668df41bf 100644
--- a/atest/testdata/standard_libraries/builtin/reload_library/StaticLibrary.py
+++ b/atest/testdata/standard_libraries/builtin/reload_library/StaticLibrary.py
@@ -7,5 +7,6 @@ def add_static_keyword(self, name):
def f(x):
"""This doc for static"""
return x
+
setattr(self, name, f)
BuiltIn().reload_library(self)
diff --git a/atest/testdata/standard_libraries/builtin/reload_library/module_library.py b/atest/testdata/standard_libraries/builtin/reload_library/module_library.py
index 17f4f5bc637..7f40e6448db 100644
--- a/atest/testdata/standard_libraries/builtin/reload_library/module_library.py
+++ b/atest/testdata/standard_libraries/builtin/reload_library/module_library.py
@@ -2,5 +2,5 @@ def add_module_keyword(name):
def f(x):
"""This doc for module"""
return x
- globals()[name] = f
+ globals()[name] = f
diff --git a/atest/testdata/standard_libraries/builtin/run_keyword.robot b/atest/testdata/standard_libraries/builtin/run_keyword.robot
index 4b8557fae3c..f97c8c80f5b 100644
--- a/atest/testdata/standard_libraries/builtin/run_keyword.robot
+++ b/atest/testdata/standard_libraries/builtin/run_keyword.robot
@@ -73,7 +73,20 @@ With library keyword accepting embedded arguments as variables containing object
Run Keyword Embedded "${OBJECT}" in library
Run Keyword Embedded object "${OBJECT}" in library
-Run Keyword In For Loop
+Embedded arguments matching only after replacing variables
+ VAR ${arg} "arg"
+ Run Keyword Embedded ${arg}
+ Run Keyword Embedded ${arg} in library
+
+Exact match after replacing variables has higher precedence than embedded arguments
+ VAR ${not} not
+ Run Keyword Embedded "${not}"
+ Run Keyword Embedded "${{'NOT'}}" in library
+ VAR ${not} "not"
+ Run Keyword Embedded ${not}
+ Run Keyword Embedded ${not} in library
+
+Run Keyword In FOR Loop
[Documentation] FAIL Expected failure in For Loop
FOR ${kw} ${arg1} ${arg2} IN
... Log hello from for loop INFO
@@ -92,16 +105,16 @@ Run Keyword With Test Timeout Passing
Run Keyword Log Timeout is not exceeded
Run Keyword With Test Timeout Exceeded
- [Documentation] FAIL Test timeout 1 second 234 milliseconds exceeded.
- [Timeout] 1234 milliseconds
+ [Documentation] FAIL Test timeout 300 milliseconds exceeded.
+ [Timeout] 0.3 s
Run Keyword Log Before Timeout
- Run Keyword Sleep 1.3s
+ Run Keyword Sleep 5 s
Run Keyword With KW Timeout Passing
Run Keyword Timeoutted UK Passing
Run Keyword With KW Timeout Exceeded
- [Documentation] FAIL Keyword timeout 300 milliseconds exceeded.
+ [Documentation] FAIL Keyword timeout 50 milliseconds exceeded.
Run Keyword Timeoutted UK Timeouting
Run Keyword With Invalid Keyword Name
@@ -122,7 +135,7 @@ Timeoutted UK Passing
No Operation
Timeoutted UK Timeouting
- [Timeout] 300 milliseconds
+ [Timeout] 50 milliseconds
Sleep 1 second
Embedded "${arg}"
@@ -131,3 +144,6 @@ Embedded "${arg}"
Embedded object "${obj}"
Log ${obj}
Should Be Equal ${obj.name} Robot
+
+Embedded "not"
+ Log Nothing embedded in this user keyword!
diff --git a/atest/testdata/standard_libraries/builtin/set_library_search_order/TestLibrary.py b/atest/testdata/standard_libraries/builtin/set_library_search_order/TestLibrary.py
index 73e90f84054..b223827f751 100644
--- a/atest/testdata/standard_libraries/builtin/set_library_search_order/TestLibrary.py
+++ b/atest/testdata/standard_libraries/builtin/set_library_search_order/TestLibrary.py
@@ -1,6 +1,6 @@
class TestLibrary:
- def __init__(self, name='TestLibrary'):
+ def __init__(self, name="TestLibrary"):
self.name = name
def get_name(self):
@@ -11,10 +11,14 @@ def get_name(self):
def no_operation(self):
return self.name
+
def get_name_with_search_order(name):
- raise AssertionError('Should not be run due to search order '
- 'having higher precedence.')
+ raise AssertionError(
+ "Should not be run due to search order having higher precedence."
+ )
+
def get_best_match_ever_with_search_order():
- raise AssertionError('Should not be run due to search order '
- 'having higher precedence.')
+ raise AssertionError(
+ "Should not be run due to search order having higher precedence."
+ )
diff --git a/atest/testdata/standard_libraries/builtin/set_library_search_order/embedded.py b/atest/testdata/standard_libraries/builtin/set_library_search_order/embedded.py
index 29eb5f7a4c2..1c6ac36d882 100644
--- a/atest/testdata/standard_libraries/builtin/set_library_search_order/embedded.py
+++ b/atest/testdata/standard_libraries/builtin/set_library_search_order/embedded.py
@@ -1,17 +1,22 @@
from robot.api.deco import keyword
-@keyword('No ${Ope}ration')
+@keyword("No ${Ope}ration")
def no_operation(ope):
- raise AssertionError('Should not be run due to keywords with normal '
- 'arguments having higher precedence.')
+ raise AssertionError(
+ "Should not be run due to keywords with normal "
+ "arguments having higher precedence."
+ )
-@keyword('Get ${Name}')
+@keyword("Get ${Name}")
def get_name(name):
- raise AssertionError('Should not be run due to keywords with normal '
- 'arguments having higher precedence.')
+ raise AssertionError(
+ "Should not be run due to keywords with normal "
+ "arguments having higher precedence."
+ )
-@keyword('Get ${Name} With Search Order')
+
+@keyword("Get ${Name} With Search Order")
def get_name_with_search_order(name):
return "embedded"
diff --git a/atest/testdata/standard_libraries/builtin/set_library_search_order/embedded2.py b/atest/testdata/standard_libraries/builtin/set_library_search_order/embedded2.py
index 81f91bbe08a..cf96cf88132 100644
--- a/atest/testdata/standard_libraries/builtin/set_library_search_order/embedded2.py
+++ b/atest/testdata/standard_libraries/builtin/set_library_search_order/embedded2.py
@@ -1,17 +1,17 @@
from robot.api.deco import keyword
-@keyword('Get ${Match} With Search Order')
-def get_best_match_ever_with_search_order(Match):
- raise AssertionError('Should not be run due to a better match'
- 'in same library.')
+@keyword("Get ${Match} With Search Order")
+def get_best_match_ever_with_search_order_1(match):
+ raise AssertionError("Should not be run due to a better matchin same library.")
-@keyword('Get Best ${Match:\w+} With Search Order')
-def get_best_match_with_search_order(Match):
- raise AssertionError('Should not be run due to a better match'
- 'in same library.')
-@keyword('Get Best ${Match} With Search Order')
-def get_best_match_with_search_order(Match):
- assert Match == "Match Ever"
+@keyword("Get Best ${Match:\w+} With Search Order")
+def get_best_match_with_search_order_2(match):
+ raise AssertionError("Should not be run due to a better matchin same library.")
+
+
+@keyword("Get Best ${Match} With Search Order")
+def get_best_match_with_search_order_3(match):
+ assert match == "Match Ever"
return "embedded2"
diff --git a/atest/testdata/standard_libraries/builtin/set_suite_metadata.robot b/atest/testdata/standard_libraries/builtin/set_suite_metadata.robot
index 838ca0a59c4..57216f60cec 100644
--- a/atest/testdata/standard_libraries/builtin/set_suite_metadata.robot
+++ b/atest/testdata/standard_libraries/builtin/set_suite_metadata.robot
@@ -25,11 +25,20 @@ Append to value
Metadata variable should have value To Append Original is continued
Set Suite Metadata TOAPPEND \n\ntwice! append=please
Metadata variable should have value To Append Original is continued \n\ntwice!
+ Set Suite Metadata Version 1.0 append please separator=,
+ Metadata variable should have value Version 1.0
+ Set Suite Metadata version 2.0 append please separator=/
+ Metadata variable should have value Version 1.0/2.0
+ Set Suite Metadata ver sion 3.0 append please separator=/
+ Metadata variable should have value Version 1.0/2.0/3.0
Set top-level suite metadata
Set Suite Metadata New metadata Metadata for top=yes
Set Suite Metadata newmetadata top level suite append top
Metadata variable should have value New metadata Set in test
+ Set Suite Metadata Separator ${2} append=yes top=yes separator=/
+ Set Suite Metadata Separator top append top separator=
+ Set Suite Metadata Separator level append top separator=**
Non-ASCII and non-string names and values
Set Suite Metadata ${42} ${1}
diff --git a/atest/testdata/standard_libraries/builtin/set_test_message.robot b/atest/testdata/standard_libraries/builtin/set_test_message.robot
index 76df9df2194..4912046b61d 100644
--- a/atest/testdata/standard_libraries/builtin/set_test_message.robot
+++ b/atest/testdata/standard_libraries/builtin/set_test_message.robot
@@ -11,10 +11,12 @@ Reset Message
[Teardown] Should Be Equal ${TEST MESSAGE} My Real Test Message
Append To Message
- [Documentation] PASS My & its continuation <>
+ [Documentation] PASS My & its continuation <>1,\n2
Set Test Message My append please
Set Test Message & its continuation <> append=please
- [Teardown] Should Be Equal ${TEST MESSAGE} My & its continuation <>
+ Set Test Message 1 append=yes separator=
+ Set Test Message \n2 append=yup separator=,
+ [Teardown] Should Be Equal ${TEST MESSAGE} My & its continuation <>1,\n2
Set Non-ASCII Message
[Documentation] PASS Hyvää yötä & huomenta!
@@ -110,6 +112,21 @@ Test Message Variable Reacts On Set Test Message
Pass_Execution Initial Test Message
[Teardown] Check Test Message Variable Behavior Is Correct
+Append HTML to non-HTML with separator
+ [Documentation] PASS *HTML* A non HTML <message>&its HTML continuation
+ Set Test Message A non HTML
+ Set Test Message *HTML* its HTML continuation append=true separator=&
+
+Append non-HTML to HTML with separator
+ [Documentation] PASS *HTML* A HTML message<\br>its non-HTML <continuation>
+ Set Test Message *HTML* A HTML message
+ Set Test Message its non-HTML append=True separator=<\br>
+
+Append HTML to HTML with separator
+ [Documentation] PASS *HTML* A HTML message && its HTML continuation
+ Set Test Message *HTML* A HTML message
+ Set Test Message *HTML* & its HTML continuation append=yeah separator=${SPACE}&
+
*** Keywords ***
Set Message In Teardown And Fail Afterwards
Set Test Message My message before failure
diff --git a/atest/testdata/standard_libraries/builtin/setting_variables/__init__.robot b/atest/testdata/standard_libraries/builtin/setting_variables/__init__.robot
index 4e0b0fb9d32..2b544dccda2 100644
--- a/atest/testdata/standard_libraries/builtin/setting_variables/__init__.robot
+++ b/atest/testdata/standard_libraries/builtin/setting_variables/__init__.robot
@@ -7,6 +7,7 @@ ${PARENT SUITE VAR TO RESET} Initial value
*** Keywords ***
My Setup
+ Set Test Variable $parent_suite_setup_test_var New in RF 7.2!
Set Suite Variable $parent_suite_setup_suite_var Set in __init__
Set Suite Variable &parent_suite_setup_suite_var_2 children=true children=false
Set Suite Variable $parent_suite_setup_child_suite_var_1 Set in __init__ children=true
@@ -19,6 +20,7 @@ My Setup
Check Variables
My Teardown
+ Should Be Equal ${parent_suite_setup_test_var} New in RF 7.2!
Should Be Equal ${parent_suite_setup_suite_var} Set in __init__
Should Be True ${parent_suite_setup_suite_var_2} == {'children': 'true'}
Should Be Equal ${parent_suite_setup_child_suite_var_1} Set in __init__
@@ -31,15 +33,17 @@ My Teardown
Should Be Equal ${cli_var_3} New value 3
Should Be Equal ${PARENT SUITE VAR TO RESET} Set using Set Global Variable
Should Be Equal ${NEW GLOBAL VAR} ${42}
- Check Variables Overridden by global Set in test!
+ Set Test Variable $parent_suite_setup_test_var Overridden in teardown
+ Check Variables Overridden in teardown Overridden by global Set in test!
Check Variables
- [Arguments] ${override1}=${{['Set in', '__init__']}} ${override2}=Orig
+ [Arguments] ${override1}=New in RF 7.2! ${override2}=${{['Set in', '__init__']}} ${override3}=Orig
+ Should Be Equal ${parent_suite_setup_test_var} ${override1}
Should Be Equal ${parent_suite_setup_suite_var} Set in __init__
Should Be Equal ${parent_suite_setup_suite_var_2} ${{{'children': 'true'}}}
Should Be Equal ${parent_suite_setup_child_suite_var_1} Set in __init__
- Should Be Equal ${parent_suite_setup_child_suite_var_2} ${override1}
+ Should Be Equal ${parent_suite_setup_child_suite_var_2} ${override2}
Should Be Equal ${parent_suite_setup_child_suite_var_3} ${{{'Set': 'in __init__'}}}
Should Be Equal ${parent_suite_setup_global_var} Set in __init__
- Should Be Equal ${parent_suite_setup_global_var_to_reset} ${override2}
+ Should Be Equal ${parent_suite_setup_global_var_to_reset} ${override3}
Should Be Equal ${VARIABLE TABLE IN VARIABLES 2 (1)} Set by suite setup in "__init__.robot"
diff --git a/atest/testdata/standard_libraries/builtin/setting_variables/variables.robot b/atest/testdata/standard_libraries/builtin/setting_variables/variables.robot
index c43c4eac601..ec904dbe4e0 100644
--- a/atest/testdata/standard_libraries/builtin/setting_variables/variables.robot
+++ b/atest/testdata/standard_libraries/builtin/setting_variables/variables.robot
@@ -9,6 +9,7 @@ Library Collections
${SCALAR} Hi tellus
@{LIST} Hello world
&{DICT} key=value foo=bar
+${SUITE} default
${PARENT SUITE SETUP CHILD SUITE VAR 1} This is overridden by __init__
${SCALAR LIST ERROR}
... Setting list value to scalar variable '\${SCALAR}' is not
@@ -205,6 +206,23 @@ Set Task Variable as alias for Set Test Variable
Set Task Variable ${TEST VAR} Set again in test level
Test Variable Should Be Set To Set again in test level
+Test variables set on suite level is not seen in tests
+ Variable Should Not Exist $parent_suite_setup_test_var
+ Variable Should Not Exist $suite_setup_test_var
+ Variable Should Not Exist $suite_setup_test_var_to_be_overridden_by_suite_var
+ Variable Should Not Exist $suite_setup_test_var_to_be_overridden_by_global_var
+ Set Suite Variable \${SUITE setup TEST var to be overridden by SUITE var} Overridded by suite variable!
+ Set Suite Variable \${SUITE setup TEST var_to_be overridden by GLOBAL var} Overridded by global variable!
+ Should Be Equal ${suite_setup_test_var_to_be_overridden_by_suite_var} Overridded by suite variable!
+ Should Be Equal ${suite_setup_test_var_to_be_overridden_by_global_var} Overridded by global variable!
+
+Test variable set on suite level does not hide existing suite variable
+ Should Be Equal ${SUITE} default
+
+Test variable set on suite level can be overridden as suite variable
+ Should Be Equal ${suite_setup_test_var_to_be_overridden_by_suite_var} Overridded by suite variable!
+ Should Be Equal ${suite_setup_test_var_to_be_overridden_by_global_var} Overridded by global variable!
+
Set Suite Variable 1
[Documentation] FAIL Variable '\${non_existing}' not found.
Variable Should Not Exist $parent_suite_setup_suite_var
@@ -545,6 +563,11 @@ Setting scalar global variable with list value is not possible 2
*** Keywords ***
My Suite Setup
${suite_setup_local_var} = Set Variable Variable available only locally in suite setup
+ Set Test Variable $suite_setup_test_var New in RF 7.2!
+ Set Test Variable $suite_setup_test_var_to_be_overridden_by_suite_var Will be overridden
+ Set Test Variable $suite_setup_test_var_to_be_overridden_by_global_var Will be overridden
+ Should Be Equal ${SUITE} default
+ Set Test Variable ${SUITE} suite level test variable
Set Suite Variable $suite_setup_suite_var Suite var set in suite setup
@{suite_setup_suite_var_list} = Create List Suite var set in suite setup
Set Suite Variable @suite_setup_suite_var_list
@@ -556,6 +579,7 @@ My Suite Setup
Should Be True ${suite_setup_suite_var_list} == [ 'Suite var set in', 'suite setup' ]
Should Be Equal ${suite_setup_global_var} Global var set in suite setup
Should Be True ${suite_setup_global_var_list} == [ 'Global var set in', 'suite setup' ]
+ Variable Should Not Exist $parent_suite_setup_test_var
Variable Should Not Exist $parent_suite_setup_suite_var
Variable Should Not Exist $parent_suite_setup_suite_var_2
Should Be Equal ${parent_suite_setup_global_var} Set in __init__
@@ -569,6 +593,10 @@ My Suite Setup
My Suite Teardown
Set Suite Variable $suite_teardown_suite_var Suite var set in suite teardown
+ Should Be Equal ${suite_setup_test_var} New in RF 7.2!
+ Should Be Equal ${suite_setup_test_var_to_be_overridden_by_suite_var} Overridded by suite variable!
+ Should Be Equal ${suite_setup_test_var_to_be_overridden_by_global_var} Overridded by global variable!
+ Should Be Equal ${SUITE} suite level test variable
Should Be Equal ${suite_setup_suite_var} Suite var set in suite setup
Should Be Equal ${test_level_suite_var} Suite var set in test
Should Be Equal ${uk_level_suite_var} Suite var set in user keyword
diff --git a/atest/testdata/standard_libraries/builtin/setting_variables/variables2.robot b/atest/testdata/standard_libraries/builtin/setting_variables/variables2.robot
index a3a327233f1..366ad31b4f5 100644
--- a/atest/testdata/standard_libraries/builtin/setting_variables/variables2.robot
+++ b/atest/testdata/standard_libraries/builtin/setting_variables/variables2.robot
@@ -5,7 +5,8 @@ ${VARIABLE TABLE IN VARIABLES 2 (3)} Initial value
*** Test Cases ***
Test Variables Set In One Suite Are Not Available In Another
- [Documentation] Also checks that variables created in the variable table of the other suite are not available here.
+ [Documentation] Also checks that variables created in the variable section of the other suite
+ ... or test variables created on suite level there are not available here.
Variable Should Not Exist $new_var
Variable Should Not Exist $uk_var_1
Variable Should Not Exist $uk_var_2
@@ -13,6 +14,8 @@ Test Variables Set In One Suite Are Not Available In Another
Variable Should Not Exist @uk_var_3
Variable Should Not Exist \${scalar}
Variable Should Not Exist \@{list}
+ Variable Should Not Exist $parent_suite_setup_test_var
+ Variable Should Not Exist $suite_setup_test_var
Suite Variables Set In One Suite Are Not Available In Another
Variable Should Not Exist \${suite_setup_suite_var}
diff --git a/atest/testdata/standard_libraries/builtin/setting_variables/variables3.robot b/atest/testdata/standard_libraries/builtin/setting_variables/variables3.robot
deleted file mode 100644
index e5e1f1f59aa..00000000000
--- a/atest/testdata/standard_libraries/builtin/setting_variables/variables3.robot
+++ /dev/null
@@ -1,28 +0,0 @@
-*** Settings ***
-Suite Setup Suite Setup
-Suite Teardown Suite Teardown
-
-*** Test Cases ***
-Set Test Variable cannot be used in suite setup or teardown
- [Documentation] FAIL
- ... Parent suite teardown failed:
- ... Several failures occurred:
- ...
- ... 1) Cannot set test variable when no test is started.
- ...
- ... 2) Cannot set test variable when no test is started.
- No Operation
-
-*** Keywords ***
-Suite Setup
- TRY
- Set Test Variable ${VAR} Fails!
- EXCEPT Cannot set test variable when no test is started.
- No Operation
- ELSE
- Fail Should have failed!
- END
-
-Suite Teardown
- Set Test Variable ${VAR} Fails!
- Set Test Variable ${VAR} Fails again!
diff --git a/atest/testdata/standard_libraries/builtin/should_be_equal.robot b/atest/testdata/standard_libraries/builtin/should_be_equal.robot
index b6de4e76e09..5c2595ab8dc 100644
--- a/atest/testdata/standard_libraries/builtin/should_be_equal.robot
+++ b/atest/testdata/standard_libraries/builtin/should_be_equal.robot
@@ -254,7 +254,7 @@ formatter=repr/ascii with multiline and non-ASCII characters
Å\nÄ\n\Ö\n Å\nA\u0308\n\Ö\n formatter=ascii
Invalid formatter
- [Documentation] FAIL ValueError: Invalid formatter 'invalid'. Available 'str', 'repr', 'ascii', 'len', and 'type'.
+ [Documentation] FAIL ValueError: Invalid formatter 'invalid'. Available 'str', 'repr', 'ascii', 'len' and 'type'.
1 1 formatter=invalid
Tuple and list with same items fail
diff --git a/atest/testdata/standard_libraries/builtin/should_be_equal_type_conversion.robot b/atest/testdata/standard_libraries/builtin/should_be_equal_type_conversion.robot
new file mode 100644
index 00000000000..5126138d2c3
--- /dev/null
+++ b/atest/testdata/standard_libraries/builtin/should_be_equal_type_conversion.robot
@@ -0,0 +1,68 @@
+*** Settings ***
+Test Template Should Be Equal
+
+*** Test Cases ***
+Convert second argument using `type`
+ ${42} 42 type=int
+ 42 ${42} type=${{str}}
+ ${False} no type=Boolean
+ ${{[1, 2, 'x']}} [1, 2.0, 'x'] type=list[int|str]
+ Cat cat type=Literal["Dog", "Cat", "Cow"]
+
+Automatic `type`
+ ${42} 42 type=auto
+ 42 ${42} type=AUTO
+ ${False} no type=Auto
+ ${{[1, 2, 3]}} [1, 2, 3] type=AuTo
+
+Automatic `type` doesn't handle nested types
+ [Documentation] FAIL [1, 2, 3] != [1, 2, '3']
+ ${{[1, 2, 3]}} [1, 2, '3'] type=auto
+
+First argument must match `type`
+ [Documentation] FAIL
+ ... Several failures occurred:
+ ...
+ ... 1) ValueError: Argument 'first' got value '42' that does not match type 'int'.
+ ...
+ ... 2) ValueError: Argument 'first' got value [1, 2] that does not match type 'list[str]'.
+ 42 42 type=int
+ ${{[1, 2]}} ['1', '2'] type=list[str]
+
+Conversion fails with `type`
+ [Documentation] FAIL ValueError: Argument 'second' got value 'bad' that cannot be converted to integer.
+ ${42} bad type=int
+
+Invalid type with `type`
+ [Documentation] FAIL TypeError: Unrecognized type 'bad'.
+ ${42} whatever type=bad
+
+Convert both arguments using `types`
+ ${42} 42 types=int
+ 42 ${42} types=${{str}}
+ ${False} no types=Boolean
+ ${{[1, 2, 'x']}} [1, 2.0, 'x'] types=list[int|str]
+ 42 42 types=int
+ ${{[1, 2]}} ['1', '2'] types=list[str]
+
+Conversion fails with `types`
+ [Documentation] FAIL
+ ... Several failures occurred:
+ ...
+ ... 1) ValueError: Argument 'first' got value 'bad' that cannot be converted to integer.
+ ...
+ ... 2) ValueError: Argument 'second' got value 'bad' that cannot be converted to decimal.
+ bad 2 types=int
+ 1 bad types=decimal
+
+Invalid type with `types`
+ [Documentation] FAIL TypeError: Unrecognized type 'oooops'.
+ ${42} whatever types=oooops
+
+Cannot use both `type` and `types`
+ [Documentation] FAIL TypeError: Cannot use both 'type' and 'types' arguments.
+ 1 1 type=int types=int
+
+Automatic type doesn't work with `types`
+ [Documentation] FAIL TypeError: Unrecognized type 'auto'.
+ ${42} ${42} types=auto
diff --git a/atest/testdata/standard_libraries/builtin/should_contain.robot b/atest/testdata/standard_libraries/builtin/should_contain.robot
index 5d2b31bdc91..47bc3dbeb02 100644
--- a/atest/testdata/standard_libraries/builtin/should_contain.robot
+++ b/atest/testdata/standard_libraries/builtin/should_contain.robot
@@ -100,6 +100,34 @@ Should Contain and collapse spaces
${LIST4} \tb\n collapse_spaces=TRUE
${LIST4} \tc\n collapse_spaces=TRUE
+Should Contain with bytes
+ [Documentation] FAIL Several failures occurred:
+ ...
+ ... 1) ValueError: '\u0666' cannot be encoded into bytes.
+ ...
+ ... 2) ValueError: Byte must be in range 0-255, got 666.
+ [Template] Should Contain
+ ${{b'hyva'}} yva
+ ${{b'h\xfcv\xe4'}} üvä
+ ${{bytes([0, 1, 2])}} \x01
+ ${{bytes([0, 1, 2])}} ${1}
+ ${{bytes([0, 1, 2])}} \u0666
+ ${{bytes([0, 1, 2])}} ${666}
+
+Should Contain with bytearray
+ [Documentation] FAIL Several failures occurred:
+ ...
+ ... 1) ValueError: '\u0666' cannot be encoded into bytes.
+ ...
+ ... 2) ValueError: Byte must be in range 0-255, got 666.
+ [Template] Should Contain
+ ${{bytearray(b'hyva')}} yva
+ ${{bytearray(b'h\xfcv\xe4')}} üvä
+ ${{bytearray([0, 1, 2])}} \x01
+ ${{bytearray([0, 1, 2])}} ${1}
+ ${{bytearray([0, 1, 2])}} \u0666
+ ${{bytearray([0, 1, 2])}} ${666}
+
Should Not Contain
[Documentation] FAIL 'Hello yet again' contains 'yet'
[Template] Should Not Contain
diff --git a/atest/testdata/standard_libraries/builtin/should_contain_any.robot b/atest/testdata/standard_libraries/builtin/should_contain_any.robot
index 4c76979ac88..00710682392 100644
--- a/atest/testdata/standard_libraries/builtin/should_contain_any.robot
+++ b/atest/testdata/standard_libraries/builtin/should_contain_any.robot
@@ -5,9 +5,9 @@ Variables variables_to_verify.py
Should Contain Any
[Template] Should Contain Any
abcdefg c
- åäö x y z ä b
- ${LIST} x y z e b c
- ${DICT} x y z a b c
+ åäö x y z=3 ä b
+ ${LIST} x y z=3 e b c
+ ${DICT} x y z=3 a b c
${LIST} 41 ${42} 43
Should Contain Any failing
@@ -119,12 +119,12 @@ Should Contain Any and collapse spaces
${DICT 5} e \n \t e collapse_spaces=TRUE
Should Contain Any without items fails
- [Documentation] FAIL One or more items required.
+ [Documentation] FAIL One or more item required.
Should Contain Any foo
Should Contain Any with invalid configuration
- [Documentation] FAIL Unsupported configuration parameters: 'bad parameter' and 'шта'.
- Should Contain Any abcdefg + \= msg=Message bad parameter=True шта=?
+ [Documentation] FAIL Keyword 'BuiltIn.Should Contain Any' got unexpected named arguments 'bad parameter' and 'шта'.
+ Should Contain Any abcdefg + ok=True msg=Message bad parameter=True шта=?
Should Not Contain Any
[Template] Should Not Contain Any
@@ -250,9 +250,9 @@ Should Not Contain Any and collapse spaces
${DICT 5} e\te collapse_spaces=TRUE
Should Not Contain Any without items fails
- [Documentation] FAIL One or more items required.
+ [Documentation] FAIL One or more item required.
Should Not Contain Any foo
Should Not Contain Any with invalid configuration
- [Documentation] FAIL Unsupported configuration parameter: 'bad parameter'.
- Should Not Contain Any abcdefg + \= msg=Message bad parameter=True
+ [Documentation] FAIL Keyword 'BuiltIn.Should Not Contain Any' got unexpected named argument 'bad parameter'.
+ Should Not Contain Any abcdefg + ok=True msg=Message bad parameter=True
diff --git a/atest/testdata/standard_libraries/builtin/times.py b/atest/testdata/standard_libraries/builtin/times.py
index 5fd1f2f3c2d..966985e09aa 100644
--- a/atest/testdata/standard_libraries/builtin/times.py
+++ b/atest/testdata/standard_libraries/builtin/times.py
@@ -1,8 +1,10 @@
-import time
import datetime
+import time
+
def get_timestamp_from_date(*args):
return int(time.mktime(datetime.datetime(*(int(arg) for arg in args)).timetuple()))
+
def get_current_time_zone():
return time.altzone if time.localtime().tm_isdst else time.timezone
diff --git a/atest/testdata/standard_libraries/builtin/used_in_custom_libs_and_listeners.robot b/atest/testdata/standard_libraries/builtin/used_in_custom_libs_and_listeners.robot
index 7ba9097c329..b50af002a5a 100644
--- a/atest/testdata/standard_libraries/builtin/used_in_custom_libs_and_listeners.robot
+++ b/atest/testdata/standard_libraries/builtin/used_in_custom_libs_and_listeners.robot
@@ -5,7 +5,7 @@ Resource UseBuiltInResource.robot
*** Test Cases ***
Keywords Using BuiltIn
- Log Debug Message
+ Log Messages And Set Log Level
${name} = Get Test Name
Should Be Equal ${name} ${TESTNAME}
Set Secret Variable
@@ -16,14 +16,14 @@ Listener Using BuiltIn
Should Be Equal ${SET BY LISTENER} quux
Use 'Run Keyword' with non-Unicode values
- Use Run Keyword with non Unicode values
+ Use Run Keyword with non string values
Use BuiltIn keywords with timeouts
[Timeout] 1 day
- Log Debug Message
+ Log Messages And Set Log Level
Set Secret Variable
Should Be Equal ${secret} *****
- Use Run Keyword with non Unicode values
+ Use Run Keyword with non string values
User keyword used via 'Run Keyword'
User Keyword via Run Keyword
@@ -32,3 +32,22 @@ User keyword used via 'Run Keyword' with timeout and trace level
[Setup] Set Log Level TRACE
[Timeout] 1 day
User Keyword via Run Keyword
+
+Recursive 'Run Keyword' usage
+ Recursive Run Keyword 10
+
+Recursive 'Run Keyword' usage with timeout
+ [Documentation] FAIL Test timeout 10 milliseconds exceeded.
+ [Timeout] 0.01 s
+ [Setup] NONE
+ Recursive Run Keyword 1000
+
+Timeout when running keyword that logs huge message
+ [Documentation] FAIL Test timeout 100 milliseconds exceeded.
+ [Timeout] 0.1 s
+ Run keyword that logs huge message until timeout
+
+Timeout in parent keyword after running keyword
+ [Documentation] FAIL Test timeout 100 milliseconds exceeded.
+ [Timeout] 0.1 s
+ Timeout in parent keyword after running keyword
diff --git a/atest/testdata/standard_libraries/builtin/variable.py b/atest/testdata/standard_libraries/builtin/variable.py
index fbd2a37e754..b70cfadfaa9 100644
--- a/atest/testdata/standard_libraries/builtin/variable.py
+++ b/atest/testdata/standard_libraries/builtin/variable.py
@@ -7,4 +7,4 @@ def __str__(self):
return self.name
-OBJECT = Object('Robot')
+OBJECT = Object("Robot")
diff --git a/atest/testdata/standard_libraries/builtin/variables_to_import_1.py b/atest/testdata/standard_libraries/builtin/variables_to_import_1.py
index 9e6a303df87..73fdc5b85ad 100644
--- a/atest/testdata/standard_libraries/builtin/variables_to_import_1.py
+++ b/atest/testdata/standard_libraries/builtin/variables_to_import_1.py
@@ -1,2 +1,2 @@
-IMPORT_VARIABLES_1 = 'Simple variable file'
+IMPORT_VARIABLES_1 = "Simple variable file"
COMMON_VARIABLE = 1
diff --git a/atest/testdata/standard_libraries/builtin/variables_to_import_2.py b/atest/testdata/standard_libraries/builtin/variables_to_import_2.py
index 4f51d2ed04f..8e075de134b 100644
--- a/atest/testdata/standard_libraries/builtin/variables_to_import_2.py
+++ b/atest/testdata/standard_libraries/builtin/variables_to_import_2.py
@@ -1,4 +1,6 @@
-def get_variables(arg1, arg2='default'):
- return {'IMPORT_VARIABLES_2': 'Dynamic variable file',
- 'IMPORT_VARIABLES_2_ARGS': '%s %s' % (arg1, arg2),
- 'COMMON VARIABLE': 2}
+def get_variables(arg1, arg2="default"):
+ return {
+ "IMPORT_VARIABLES_2": "Dynamic variable file",
+ "IMPORT_VARIABLES_2_ARGS": f"{arg1} {arg2}",
+ "COMMON VARIABLE": 2,
+ }
diff --git a/atest/testdata/standard_libraries/builtin/variables_to_verify.py b/atest/testdata/standard_libraries/builtin/variables_to_verify.py
index e17bd1d779c..5196043d51c 100644
--- a/atest/testdata/standard_libraries/builtin/variables_to_verify.py
+++ b/atest/testdata/standard_libraries/builtin/variables_to_verify.py
@@ -3,27 +3,27 @@
def get_variables():
variables = dict(
- BYTES_WITHOUT_NON_ASCII=b'hyva',
- BYTES_WITH_NON_ASCII=b'\xe4',
+ BYTES_WITHOUT_NON_ASCII=b"hyva",
+ BYTES_WITH_NON_ASCII=b"\xe4",
TUPLE_0=(),
- TUPLE_1=('a',),
- TUPLE_2=('a', 2),
- TUPLE_3=('a', 'b', 'c'),
- LIST=['a', 'b', 'cee', 'b', 42],
+ TUPLE_1=("a",),
+ TUPLE_2=("a", 2),
+ TUPLE_3=("a", "b", "c"),
+ LIST=["a", "b", "cee", "b", 42],
LIST_0=[],
- LIST_1=['a'],
- LIST_2=['a', 2],
- LIST_3=['a', 'b', 'c'],
- LIST_4=['\ta', '\na', 'b ', 'b \t', '\tc\n'],
- DICT={'a': 1, 'A': 2, 'ä': 3, 'Ä': 4},
- ORDERED_DICT=OrderedDict([('a', 1), ('A', 2), ('ä', 3), ('Ä', 4)]),
+ LIST_1=["a"],
+ LIST_2=["a", 2],
+ LIST_3=["a", "b", "c"],
+ LIST_4=["\ta", "\na", "b ", "b \t", "\tc\n"],
+ DICT={"a": 1, "A": 2, "ä": 3, "Ä": 4},
+ ORDERED_DICT=OrderedDict([("a", 1), ("A", 2), ("ä", 3), ("Ä", 4)]),
DICT_0={},
- DICT_1={'a': 1},
- DICT_2={'a': 1, 2: 'b'},
- DICT_3={'a': 1, 'b': 2, 'c': 3},
- DICT_4={'\ta': 1, 'a b': 2, ' c': 3, 'dd\n\t': 4, '\nak \t': 5},
- DICT_5={' a': 0, '\ta': 1, 'a\t': 2, '\nb': 3, 'd\t': 4, '\td\n': 5, 'e e': 6},
- PREPR_DICT1="{'a': 1}"
+ DICT_1={"a": 1},
+ DICT_2={"a": 1, 2: "b"},
+ DICT_3={"a": 1, "b": 2, "c": 3},
+ DICT_4={"\ta": 1, "a b": 2, " c": 3, "dd\n\t": 4, "\nak \t": 5},
+ DICT_5={" a": 0, "\ta": 1, "a\t": 2, "\nb": 3, "d\t": 4, "\td\n": 5, "e e": 6},
+ PREPR_DICT1="{'a': 1}",
)
- variables['ASCII_DICT'] = ascii(variables['DICT'])
+ variables["ASCII_DICT"] = ascii(variables["DICT"])
return variables
diff --git a/atest/testdata/standard_libraries/builtin/vars_for_get_variables.py b/atest/testdata/standard_libraries/builtin/vars_for_get_variables.py
index ff1c8d46fd9..b810ab6085b 100644
--- a/atest/testdata/standard_libraries/builtin/vars_for_get_variables.py
+++ b/atest/testdata/standard_libraries/builtin/vars_for_get_variables.py
@@ -1 +1 @@
-var_in_variable_file = 'Hello, world!'
+var_in_variable_file = "Hello, world!"
diff --git a/atest/testdata/standard_libraries/collections/CollectionsHelperLibrary.py b/atest/testdata/standard_libraries/collections/CollectionsHelperLibrary.py
index 9071f0bd177..6d6ec098ab7 100644
--- a/atest/testdata/standard_libraries/collections/CollectionsHelperLibrary.py
+++ b/atest/testdata/standard_libraries/collections/CollectionsHelperLibrary.py
@@ -1,8 +1,9 @@
class DictWithoutHasKey(dict):
def has_key(self, key):
- raise NotImplementedError('Emulating collections.Mapping which '
- 'does not have `has_key`.')
+ raise NotImplementedError(
+ "Emulating collections.Mapping which does not have `has_key`."
+ )
def get_dict_without_has_key(**items):
diff --git a/atest/testdata/standard_libraries/collections/dictionaries_should_be_equal.robot b/atest/testdata/standard_libraries/collections/dictionaries_should_be_equal.robot
index 9ac15a8df24..5eebebc4f99 100644
--- a/atest/testdata/standard_libraries/collections/dictionaries_should_be_equal.robot
+++ b/atest/testdata/standard_libraries/collections/dictionaries_should_be_equal.robot
@@ -3,7 +3,8 @@ Library Collections
Library CollectionsHelperLibrary.py
*** Variables ***
-@{LIST} a B
+@{LIST_1} a B
+@{LIST_2} B a
${TUPLE} ${{'a', 'B'}}
&{D0}
&{D1} a=x
@@ -13,9 +14,14 @@ ${TUPLE} ${{'a', 'B'}}
&{D3C} A=X b=Y C=${3}
&{D9A} a=1 b=2 c=3 d=4 e=5 f=6 g=7 h=8 i=9
&{D9B} d=4 e=5 i=9 a=1 f=6 g=7 b=2 h=8 c=3
+&{D10A} d=4 e=5 i=9 a=1 f=6 g=7 b=2 h=8 c=3 list=${LIST_1}
+&{D10B} d=4 e=5 i=9 a=1 f=6 g=7 b=2 h=8 c=3 list=${LIST_2}
+&{D10C} d=4 e=5 i=9 a=1 f=6 g=7 b=2 h=8 c=3 list=${LIST_2} dict_list=@{DICTS}
+&{D10D} d=4 e=5 i=9 a=1 f=6 g=7 b=2 h=8 c=3 list=${LIST_2} dict_list=@{DICTS_1}
&{DX} a=x B=Y c=${3} ${4}=E ß=Straße
-... list=${LIST} ${TUPLE}=tuple dict=${D2}
+... list=${LIST_1} ${TUPLE}=tuple dict=${D2}
@{DICTS} ${D0} ${D1} ${D2} ${D3} ${D3B} ${D3C} ${D9A} ${D9B} ${DX}
+@{DICTS_1} ${DX} ${D9B} ${D9A} ${D3C} ${D3B} ${D3} ${D2} ${D1} ${D0}
*** Test Cases ***
Comparison with itself
@@ -134,3 +140,12 @@ Different values and custom error message with values
... Dictionary {'a': 1, 'A': 2} contains multiple keys that are normalized to 'a'. \
... Try normalizing only dictionary values like 'ignore_case=values'.
Dictionaries Should Be Equal ${{{'a': 1, 'A': 2}}} ${{{'a': 2, 'A': 1}}} ignore_case=True
+
+`ignore_value_order` set to True
+ Dictionaries Should Be Equal ${D10A} ${D10B} ignore_value_order=True
+
+`ignore_value_order` set to False and dictionaries have lists in different order
+ [Documentation] FAIL
+ ... Following keys have different values:
+ ... Key list: ['a', 'B'] != ['B', 'a']
+ Dictionaries Should Be Equal ${D10A} ${D10B}
diff --git a/atest/testdata/standard_libraries/collections/dictionary_should_contain.robot b/atest/testdata/standard_libraries/collections/dictionary_should_contain.robot
index 63138823a94..7e9efc15168 100644
--- a/atest/testdata/standard_libraries/collections/dictionary_should_contain.robot
+++ b/atest/testdata/standard_libraries/collections/dictionary_should_contain.robot
@@ -4,8 +4,10 @@ Library CollectionsHelperLibrary.py
*** Variables ***
@{LIST} a B
+@{LIST_2} B a
${TUPLE} ${{'a', 'B'}}
&{D} a=x B=Y c=${3} ${4}=E ß=Straße list=${LIST} ${TUPLE}=tuple
+&{D_2} a=x B=Y list=${LIST_2} ${TUPLE}=tuple
*** Test Cases ***
### Should (Not) Contain Key ###
@@ -244,6 +246,15 @@ Should contain sub dictionary with `ignore_case`
${D} ${{{'ss': 'Straße', ('A', 'b'): 'tuple'}}} ignore_case=key
${D} ${{{'a': 'x', 'ss': 'Straße', 'non': 'existing'}}} ignore_case=value
+Should contain sub dictionary with `ignore_value_order`
+ Dictionary Should Contain Sub Dictionary ${D} ${D_2} ignore_value_order=True
+
+Should contain sub dictionary with `ignore_value_order` set to False when dictionaries have lists in different order
+ [Documentation] FAIL
+ ... Following keys have different values:
+ ... Key list: ['a', 'B'] != ['B', 'a']
+ Dictionary Should Contain Sub Dictionary ${D} ${D_2}
+
### Misc ###
`ignore_case` when normalized keys have conflict
diff --git a/atest/testdata/standard_libraries/collections/list.robot b/atest/testdata/standard_libraries/collections/list.robot
index 91a3611f8f5..75631f7ca86 100644
--- a/atest/testdata/standard_libraries/collections/list.robot
+++ b/atest/testdata/standard_libraries/collections/list.robot
@@ -344,17 +344,21 @@ List Should Contain Sub List
List Should Contain Sub List ${LONG} ${L4}
List Should Contain Sub List With Missing Values
- [Documentation] FAIL Following values were not found from first list: 1, 1, 2, 1, 2
+ [Documentation] FAIL Following values are missing: '1', '1', '2', '1' and '2'
List Should Contain Sub List ${L4} ${LONG}
+List Should Contain Sub List When The Only Missing Value Is Empty String
+ [Documentation] FAIL Following values are missing: ''
+ List Should Contain Sub List ${L4} ${{['41', 42, '', '43']}}
+
List Should Contain Sub List With Missing Values And Own Error Message
[Documentation] FAIL My error message!
List Should Contain Sub List ${L4} ${LONG} My error message! No Values
List Should Contain Sub List With Missing Values And Own And Default Error Messages
[Documentation] FAIL My error message!
- ... Following values were not found from first list: 1, 1, 2, 1, 2
- List Should Contain Sub List ${L4} ${LONG} My error message! values=please
+ ... Following values are missing: 'x' and 'y'
+ List Should Contain Sub List ${L4} ${{'x', 'y'}} My error message! values=please
Log List With Different Log Levels
Log List ${L3}
@@ -668,6 +672,12 @@ List Should Not Contain Duplicates With Ignore Case
List Should Contain Value With Ignore Case And Nested List and Dictionary
[Setup] Create Lists For Testing Ignore Case
List Should Contain Value ${L4} value=d ignore_case=${True}
+
+Lists Should be equal with Ignore Case and Order
+ [Setup] Create Lists For Testing Ignore Case
+ [Template] Lists Should Be Equal
+ list1=${L7} list2=${L8} ignore_order=${True} ignore_case=${True}
+ list1=${L9} list2=${L10} ignore_order=${True} ignore_case=${True}
*** Keywords ***
Validate invalid argument error
@@ -745,3 +755,11 @@ Create Lists For Testing Ignore Case
Set Test Variable \${L5}
${L6} Create List ${L1} d D 3 ${D1}
Set Test Variable \${L6}
+ ${L7} Create List apple Banana cherry
+ Set Test Variable \${L7}
+ ${L8} Create List BANANA cherry APPLE
+ Set Test Variable \${L8}
+ ${L9} Create List zebra! ${EMPTY} Elephant< "Dog" "Dog"
+ Set Test Variable \${L9}
+ ${L10} Create List "dog" ZEBRA! "Dog" elephant< ${EMPTY}
+ Set Test Variable \${L10}
diff --git a/atest/testdata/standard_libraries/datetime/convert_time_input_format.robot b/atest/testdata/standard_libraries/datetime/convert_time_input_format.robot
index 46d5d73ce7b..c43268765e2 100644
--- a/atest/testdata/standard_libraries/datetime/convert_time_input_format.robot
+++ b/atest/testdata/standard_libraries/datetime/convert_time_input_format.robot
@@ -17,6 +17,12 @@ Time string 10 s 10
0.123456789 ms 0.000123456789
123 μs 0.000123
1 ns 1E-9
+ 1 week 604800
+ 2 weeks 1209600
+ 1 week 1 day 1 hour 1 min 1 sec 1 ms 694861.001
+ 2 weeks 1 day 1 hour 1 min 1 sec 1299661
+ 1w 1d 1h 1m 1s 1ms 694861.001
+ 2w 1d 1h 1m 1s 1ms 1299661.001
Number as string 10 10
0.5 0.5
diff --git a/atest/testdata/standard_libraries/datetime/datesandtimes.py b/atest/testdata/standard_libraries/datetime/datesandtimes.py
index ccdc3c9a7f4..d28d291075b 100644
--- a/atest/testdata/standard_libraries/datetime/datesandtimes.py
+++ b/atest/testdata/standard_libraries/datetime/datesandtimes.py
@@ -1,10 +1,9 @@
import time
-from datetime import date, datetime, timedelta
-
+from datetime import date as date, datetime as datetime, timedelta as timedelta
TIMEZONE = time.altzone if time.localtime().tm_isdst else time.timezone
-EPOCH = 1542892422.0 + time.timezone # 2018-11-22 13:13:42
-BIG_EPOCH = 6000000000 + time.timezone # 2160-02-18 10:40:00
+EPOCH = 1542892422.0 + time.timezone # 2018-11-22 13:13:42
+BIG_EPOCH = 6000000000 + time.timezone # 2160-02-18 10:40:00
def all_days_for_year(year):
@@ -12,19 +11,21 @@ def all_days_for_year(year):
dt = datetime(year, 1, 1)
day = timedelta(days=1)
while dt.year == year:
- yield dt.strftime('%Y-%m-%d %H:%M:%S')
+ yield dt.strftime("%Y-%m-%d %H:%M:%S")
dt += day
-def year_range(start, end, step=1, format='timestamp'):
+def year_range(start, end, step=1, format="timestamp"):
dt = datetime(int(start), 1, 1)
end = int(end)
step = int(step)
while dt.year <= end:
- if format == 'datetime':
+ if format == "datetime":
yield dt
- if format == 'timestamp':
- yield dt.strftime('%Y-%m-%d %H:%M:%S')
- if format == 'epocn':
- yield time.mktime(dt.timetuple())
+ elif format == "timestamp":
+ yield dt.strftime("%Y-%m-%d %H:%M:%S")
+ elif format == "epoch":
+ yield dt.timestamp() if dt.year != 1970 else 0
+ else:
+ raise ValueError(f"Invalid format: {format}")
dt = dt.replace(year=dt.year + step)
diff --git a/atest/testdata/standard_libraries/dialogs/dialogs.robot b/atest/testdata/standard_libraries/dialogs/dialogs.robot
index c25713fb49b..1a1937cf41f 100644
--- a/atest/testdata/standard_libraries/dialogs/dialogs.robot
+++ b/atest/testdata/standard_libraries/dialogs/dialogs.robot
@@ -10,8 +10,8 @@ ${FILLER} = Wräp < & シ${SPACE}
Pause Execution
Pause Execution Press OK button.
Pause Execution Press key.
- Pause Execution Press key.
Pause Execution Press key.
+ Pause Execution Press key.
Pause Execution With Long Line
Pause Execution Verify that the long text below is wrapped nicely.\n\n${FILLER*200}\n\nThen press OK or .
@@ -20,9 +20,8 @@ Pause Execution With Multiple Lines
Pause Execution Verify that\nthis multi\nline text\nis displayed\nnicely.\n\nʕ•ᴥ•ʔ\n\nThen press .
Execute Manual Step Passing
- Execute Manual Step Press PASS.
- Execute Manual Step Press and validate that the dialog is *NOT* closed.\n\nThen press PASS.
- Execute Manual Step Press
or
. This should not be shown!!
+ Execute Manual Step Verify the taskbar icon.\n\nPress PASS if it is ok. Invalid taskbar icon.
+ Execute Manual Step Press and validate that the dialog is *NOT* closed.\n\nThen press
or
Execute Manual Step Failing
[Documentation] FAIL Predefined error message
@@ -53,7 +52,7 @@ Get Hidden Value From User
Get Value From User Cancelled
[Documentation] FAIL No value provided by user.
Get Value From User
- ... Press Cancel.\n\nAlso verify that the default value below is not hidded.
+ ... Press Cancel.\n\nAlso verify that the default value below is not hidden.
... Default value. hidden=no
Get Value From User Exited
@@ -73,6 +72,41 @@ Get Selection From User
... This is a really long string and the window should change the size properly to content.
Should Be Equal ${value} valuë
+Get Selection From User When Default Value Provided by Index
+ ${value}= Get Selection From User
+ ... Press OK or .
+ ... value 1 value 2 value 3 value 4
+ ... default=1
+ Should Be Equal ${value} value 1
+
+Get Selection From User When Default Value Provided by String
+ ${value}= Get Selection From User
+ ... Press OK or .
+ ... xxx yyy zzz ååå äää ööö
+ ... default=ööö
+ Should Be Equal ${value} ööö
+
+Get Selection From User When Default Value Is Integer
+ ${value}= Get Selection From User
+ ... Press OK or .
+ ... -2 -1 0 1 2
+ ... default=1
+ Should Be Equal ${value} 1
+
+Get Selection From User When Default Value Index Is Out of Bounds
+ [Documentation] FAIL ValueError: Default value index is out of bounds.
+ Get Selection From User
+ ... Press OK or .
+ ... value 1 value 2 value 3 value 4
+ ... default=5
+
+Get Selection From User When Default Value Cannot Be Found
+ [Documentation] FAIL ValueError: Invalid default value 'asd'.
+ Get Selection From User
+ ... Press OK or .
+ ... value 1 value 2 value 3 value 4
+ ... default=asd
+
Get Selection From User Cancelled
[Documentation] FAIL No value provided by user.
Get Selection From User Press or . zip zap foo
@@ -115,5 +149,17 @@ Get Selections From User Exited
Multiple dialogs in a row
[Documentation] FAIL No value provided by user.
- Pause Execution Verify that dialog is closed immediately.\n\nAfter pressing OK or .
- Get Value From User Verify that dialog is closed immediately.\n\nAfter pressing Cancel or .
+ Pause Execution Press OK or and verify that dialog is closed immediately.\n\nNext dialog is opened after 1 second.
+ Sleep 1 second
+ Get Value From User Press Cancel or and verify that dialog is closed immediately.
+
+Garbage Collection In Thread Should Not Cause Problems
+ ${thread}= Evaluate threading.Thread(target=gc.collect)
+ Pause Execution Press OK or and verify that execution does not crash.
+ Call Method ${thread} start
+ Call Method ${thread} join
+
+Timeout can close dialog
+ [Documentation] FAIL Test timeout 1 second exceeded.
+ [Timeout] 1 second
+ Pause Execution Wait for timeout.
diff --git a/atest/testdata/standard_libraries/operating_system/env_vars.robot b/atest/testdata/standard_libraries/operating_system/env_vars.robot
index adb57908290..f4249506116 100644
--- a/atest/testdata/standard_libraries/operating_system/env_vars.robot
+++ b/atest/testdata/standard_libraries/operating_system/env_vars.robot
@@ -35,12 +35,12 @@ Append To Environment Variable
Append To Environment Variable With Custom Separator
Append To Environment Variable ${NAME} first separator=-
Should Be Equal %{${NAME}} first
- Append To Environment Variable ${NAME} second 3rd\=x separator=-
+ Append To Environment Variable ${NAME} second 3rd=x separator=-
Should Be Equal %{${NAME}} first-second-3rd=x
Append To Environment Variable With Invalid Config
- [Documentation] FAIL Configuration 'not=ok' or 'these=are' not accepted.
- Append To Environment Variable ${NAME} value these=are not=ok
+ [Documentation] FAIL Keyword 'OperatingSystem.Append To Environment Variable' got unexpected named argument 'not_ok'.
+ Append To Environment Variable ${NAME} value separator=value not_ok=True
Remove Environment Variable
Set Environment Variable ${NAME} Hello
diff --git a/atest/testdata/standard_libraries/operating_system/files/HelperLib.py b/atest/testdata/standard_libraries/operating_system/files/HelperLib.py
index abf930c5bdb..9dfcbfe34e3 100644
--- a/atest/testdata/standard_libraries/operating_system/files/HelperLib.py
+++ b/atest/testdata/standard_libraries/operating_system/files/HelperLib.py
@@ -1,7 +1,7 @@
from subprocess import call
-def test_env_var_in_child_process(var):
- rc = call(['python', '-c', 'import os, sys; sys.exit("%s" in os.environ)' % var])
- if rc !=1 :
- raise AssertionError("Variable '%s' did not exist in child environment" % var)
+def test_env_var_in_child_process(var):
+ rc = call(["python", "-c", f"import os, sys; sys.exit('{var}' in os.environ)"])
+ if rc != 1:
+ raise AssertionError(f"Variable '{var}' did not exist in child environment")
diff --git a/atest/testdata/standard_libraries/operating_system/files/prog.py b/atest/testdata/standard_libraries/operating_system/files/prog.py
index 9d8e2c58c55..91fed20f975 100644
--- a/atest/testdata/standard_libraries/operating_system/files/prog.py
+++ b/atest/testdata/standard_libraries/operating_system/files/prog.py
@@ -1,14 +1,14 @@
import sys
-def output(rc=0, stdout='', stderr='', count=1):
+def output(rc=0, stdout="", stderr="", count=1):
if stdout:
- sys.stdout.write((stdout+'\n') * int(count))
+ sys.stdout.write((stdout + "\n") * int(count))
if stderr:
- sys.stderr.write((stderr+'\n') * int(count))
+ sys.stderr.write((stderr + "\n") * int(count))
return int(rc)
-if __name__ == '__main__':
+if __name__ == "__main__":
rc = output(*sys.argv[1:])
sys.exit(rc)
diff --git a/atest/testdata/standard_libraries/operating_system/files/writable_prog.py b/atest/testdata/standard_libraries/operating_system/files/writable_prog.py
index ba31c707fd6..24581e16cb1 100644
--- a/atest/testdata/standard_libraries/operating_system/files/writable_prog.py
+++ b/atest/testdata/standard_libraries/operating_system/files/writable_prog.py
@@ -1,5 +1,3 @@
import sys
-
print(sys.stdin.read().upper())
-
diff --git a/atest/testdata/standard_libraries/operating_system/modified_time.robot b/atest/testdata/standard_libraries/operating_system/modified_time.robot
index f5ddaa557bf..3bb5c24c485 100644
--- a/atest/testdata/standard_libraries/operating_system/modified_time.robot
+++ b/atest/testdata/standard_libraries/operating_system/modified_time.robot
@@ -37,7 +37,7 @@ Get Modified Time Fails When Path Does Not Exist
Get Modified Time ${CURDIR}/does_not_exist
Set Modified Time Using Epoch
- [Documentation] FAIL ValueError: Epoch time must be positive (got -1).
+ [Documentation] FAIL ValueError: Epoch time must be positive, got '-1'.
Create File ${TESTFILE}
${epoch} = Evaluate 1542892422.0 + time.timezone
Set Modified Time ${TESTFILE} ${epoch}
diff --git a/atest/testdata/standard_libraries/operating_system/wait_until_library.py b/atest/testdata/standard_libraries/operating_system/wait_until_library.py
index 9fa6c54cd11..f9ceb934f0d 100644
--- a/atest/testdata/standard_libraries/operating_system/wait_until_library.py
+++ b/atest/testdata/standard_libraries/operating_system/wait_until_library.py
@@ -13,7 +13,7 @@ def remove_after_sleeping(self, *paths):
self._run_after_sleeping(remover, p)
def create_file_after_sleeping(self, path):
- self._run_after_sleeping(lambda: open(path, 'w').close())
+ self._run_after_sleeping(lambda: open(path, "w", encoding="ASCII").close())
def create_dir_after_sleeping(self, path):
self._run_after_sleeping(os.mkdir, path)
diff --git a/atest/testdata/standard_libraries/process/files/countdown.py b/atest/testdata/standard_libraries/process/files/countdown.py
index ed12fef25e9..b1e484a6eda 100644
--- a/atest/testdata/standard_libraries/process/files/countdown.py
+++ b/atest/testdata/standard_libraries/process/files/countdown.py
@@ -5,18 +5,18 @@
def countdown(path):
for i in range(10, 0, -1):
- with open(path, 'w') as f:
- f.write('%d\n' % i)
+ with open(path, "w", encoding="ASCII") as f:
+ f.write(f"{i}\n")
time.sleep(0.2)
- with open(path, 'w') as f:
- f.write('BLASTOFF')
+ with open(path, "w", encoding="ASCII") as f:
+ f.write("BLASTOFF")
-if __name__ == '__main__':
+if __name__ == "__main__":
path = sys.argv[1]
children = int(sys.argv[2]) if len(sys.argv) == 3 else 0
if children:
- subprocess.Popen([sys.executable, __file__, path, str(children-1)]).wait()
+ subprocess.Popen([sys.executable, __file__, path, str(children - 1)]).wait()
else:
countdown(path)
diff --git a/atest/testdata/standard_libraries/process/files/encoding.py b/atest/testdata/standard_libraries/process/files/encoding.py
index 4d99bd8ed1d..d89a5b4073e 100644
--- a/atest/testdata/standard_libraries/process/files/encoding.py
+++ b/atest/testdata/standard_libraries/process/files/encoding.py
@@ -1,19 +1,20 @@
-from os.path import abspath, dirname, join, normpath
import sys
+from os.path import abspath, dirname, join, normpath
curdir = dirname(abspath(__file__))
-src = normpath(join(curdir, '..', '..', '..', '..', '..', 'src'))
+src = normpath(join(curdir, "..", "..", "..", "..", "..", "src"))
sys.path.insert(0, src)
-from robot.utils.encoding import CONSOLE_ENCODING, SYSTEM_ENCODING
-
+from robot.utils import CONSOLE_ENCODING, SYSTEM_ENCODING # noqa: E402
-config = dict(arg.split(':') for arg in sys.argv[1:])
-stdout = config.get('stdout', 'hyv\xe4')
-stderr = config.get('stderr', stdout)
-encoding = config.get('encoding', 'ASCII')
-encoding = {'CONSOLE': CONSOLE_ENCODING,
- 'SYSTEM': SYSTEM_ENCODING}.get(encoding, encoding)
+config = dict(arg.split(":") for arg in sys.argv[1:])
+stdout = config.get("stdout", "hyv\xe4")
+stderr = config.get("stderr", stdout)
+encoding = config.get("encoding", "ASCII")
+encoding = {
+ "CONSOLE": CONSOLE_ENCODING,
+ "SYSTEM": SYSTEM_ENCODING,
+}.get(encoding, encoding)
sys.stdout.buffer.write(stdout.encode(encoding))
diff --git a/atest/testdata/standard_libraries/process/files/non_terminable.py b/atest/testdata/standard_libraries/process/files/non_terminable.py
index 096af2796df..4bd456e7096 100755
--- a/atest/testdata/standard_libraries/process/files/non_terminable.py
+++ b/atest/testdata/standard_libraries/process/files/non_terminable.py
@@ -1,25 +1,27 @@
+import os.path
import signal
-import time
import sys
-import os.path
-
+import time
notify_path = sys.argv[1]
+
def log(msg, *extra_streams):
for stream in (sys.stdout,) + extra_streams:
- stream.write(msg + '\n')
+ stream.write(msg + "\n")
stream.flush()
+
def ignorer(signum, frame):
- log('Ignoring signal %d.' % signum)
+ log(f"Ignoring signal {signum}.")
+
signal.signal(signal.SIGTERM, ignorer)
-if hasattr(signal, 'SIGBREAK'):
+if hasattr(signal, "SIGBREAK"):
signal.signal(signal.SIGBREAK, ignorer)
-with open(notify_path, 'w') as notify:
- log('Starting non-terminable process.', notify)
+with open(notify_path, "w", encoding="ASCII") as notify:
+ log("Starting non-terminable process.", notify)
while True:
@@ -28,4 +30,4 @@ def ignorer(signum, frame):
except IOError:
pass
if not os.path.exists(notify_path):
- log('Stopping non-terminable process.')
+ log("Stopping non-terminable process.")
diff --git a/atest/testdata/standard_libraries/process/files/script.py b/atest/testdata/standard_libraries/process/files/script.py
index 97aa337a835..1e70d19e65c 100755
--- a/atest/testdata/standard_libraries/process/files/script.py
+++ b/atest/testdata/standard_libraries/process/files/script.py
@@ -2,10 +2,10 @@
import sys
-stdout = sys.argv[1] if len(sys.argv) > 1 else 'stdout'
-stderr = sys.argv[2] if len(sys.argv) > 2 else 'stderr'
+stdout = sys.argv[1] if len(sys.argv) > 1 else "stdout"
+stderr = sys.argv[2] if len(sys.argv) > 2 else "stderr"
rc = int(sys.argv[3]) if len(sys.argv) > 3 else 0
-sys.stdout.write(stdout + '\n')
-sys.stderr.write(stderr + '\n')
+sys.stdout.write(stdout + "\n")
+sys.stderr.write(stderr + "\n")
sys.exit(rc)
diff --git a/atest/testdata/standard_libraries/process/files/timeout.py b/atest/testdata/standard_libraries/process/files/timeout.py
index 9b60171c68d..b77ed0a3661 100644
--- a/atest/testdata/standard_libraries/process/files/timeout.py
+++ b/atest/testdata/standard_libraries/process/files/timeout.py
@@ -1,14 +1,14 @@
-from sys import argv, stdout, stderr
+from sys import argv, stderr, stdout
from time import sleep
timeout = float(argv[1]) if len(argv) > 1 else 1
-stdout.write('start stdout\n')
+stdout.write("start stdout\n")
stdout.flush()
-stderr.write('start stderr\n')
+stderr.write("start stderr\n")
stderr.flush()
sleep(timeout)
-stdout.write('end stdout\n')
+stdout.write("end stdout\n")
stdout.flush()
-stderr.write('end stderr\n')
+stderr.write("end stderr\n")
stderr.flush()
diff --git a/atest/testdata/standard_libraries/process/newlines.robot b/atest/testdata/standard_libraries/process/newlines.robot
index 010b357ef40..6d2b992bfbc 100644
--- a/atest/testdata/standard_libraries/process/newlines.robot
+++ b/atest/testdata/standard_libraries/process/newlines.robot
@@ -3,18 +3,29 @@ Resource process_resource.robot
*** Test Cases ***
Trailing newline is removed
- ${result}= Run Process python -c import sys; sys.stdout.write('nothing to remove')
+ ${result}= Run Process python -c import sys; sys.stdout.write('nothing to remove')
Result should equal ${result} stdout=nothing to remove
- ${result}= Run Process python -c import sys; sys.stdout.write('one is removed\\n')
- Result should equal ${result} stdout=one is removed
- ${result}= Run Process python -c import sys; sys.stdout.write('only one is removed\\n\\n\\n')
+ ${result}= Run Process python -c import sys; sys.stdout.write('removed\\n')
+ Result should equal ${result} stdout=removed
+ ${result}= Run Process python -c import sys; sys.stdout.write('only one is removed\\n\\n\\n')
Result should equal ${result} stdout=only one is removed\n\n
Internal newlines are preserved
- ${result}= Run Process python -c "print('1\\n2\\n3')" shell=True
+ ${result}= Run Process python -c import sys; sys.stdout.write('1\\n2\\n3\\n')
Result should equal ${result} stdout=1\n2\n3
+CRLF is converted to LF
+ ${result}= Run Process python -c import sys; sys.stdout.write('1\\r\\n2\\r3\\n4')
+ # On Windows \r\n is turned \r\r\n when writing and thus the result is \r\n.
+ # Elsewhere \r\n is not changed when writing and thus the result is \n.
+ # ${\n} is \r\n or \n depending on the OS and thus works as the expected result.
+ Result should equal ${result} stdout=1${\n}2\r3\n4
+
Newlines with custom stream
- ${result}= Run Process python -c "print('1\\n2\\n3')" shell=True stdout=${STDOUT}
- Result should equal ${result} stdout=1\n2\n3 stdout_path=${STDOUT}
- [Teardown] Safe Remove File ${STDOUT}
+ ${result}= Run Process python -c import sys; sys.stdout.write('1\\n2\\n3\\n')
+ Result should equal ${result} stdout=1\n2\n3
+ ${result}= Run Process python -c import sys; sys.stdout.write('1\\n2\\r\\n3\\n') stdout=${STDOUT}
+ Result should equal ${result} stdout=1\n2${\n}3 stdout_path=${STDOUT}
+ ${output} = Get Binary File ${STDOUT}
+ Should Be Equal ${output} 1${\n}2\r${\n}3${\n} type=bytes
+ [Teardown] Safe Remove File ${STDOUT}
diff --git a/atest/testdata/standard_libraries/process/output_encoding.robot b/atest/testdata/standard_libraries/process/output_encoding.robot
index f95afc5cc7c..30b3ca57f56 100644
--- a/atest/testdata/standard_libraries/process/output_encoding.robot
+++ b/atest/testdata/standard_libraries/process/output_encoding.robot
@@ -38,9 +38,8 @@ Invalid Output Encoding Should Work Correctly
... ${stdout}=${NONE} ${stderr}=${NONE}
${result} = Run Process With Output Encoding ${encoding} ${output_encoding}
... stdout=${stdout} stderr=${stderr}
- ${expected} = Set Variable If sys.platform != 'cli' hyv\\xe4 hyvä
- Should Be Equal ${result.stderr} ${expected}
- Should Be Equal ${result.stdout} ${expected}
+ Should Be Equal ${result.stderr} hyvä
+ Should Be Equal ${result.stdout} hyvä
Run Process With Output Encoding
[Arguments] ${encoding} ${output_encoding}=${NONE}
diff --git a/atest/testdata/standard_libraries/process/process_library.robot b/atest/testdata/standard_libraries/process/process_library.robot
index 7ac2357d999..0e015bfde48 100644
--- a/atest/testdata/standard_libraries/process/process_library.robot
+++ b/atest/testdata/standard_libraries/process/process_library.robot
@@ -5,25 +5,19 @@ Test Setup Restart Suite Process If Needed
Resource process_resource.robot
*** Test Cases ***
-Library Namespace should be global
+Library namespace should be global
Process Should Be Running suite_process
Error in exit code and stderr output
${result}= Run Python Process 1/0
Result should match ${result} stderr=*ZeroDivisionError:* rc=1
-Start And Wait Process
- ${handle}= Start Python Process import time;time.sleep(0.1)
- Process Should Be Running ${handle}
- Wait For Process ${handle}
- Process Should Be Stopped ${handle}
-
-Change Current Working Directory
- ${result}= Run Process python -c import os; print(os.path.abspath(os.curdir)) cwd=.
+Change current working directory
+ ${result1}= Run Process python -c import os; print(os.path.abspath(os.curdir)) cwd=.
${result2}= Run Process python -c import os; print(os.path.abspath(os.curdir)) cwd=${{pathlib.Path('..')}}
- Should Not Be Equal ${result.stdout} ${result2.stdout}
+ Should Not Be Equal ${result1.stdout} ${result2.stdout}
-Running a process in a shell
+Run process in shell
${result}= Run Process python -c "print('hello')" shell=True
Result should equal ${result} stdout=hello
${result}= Run Process python -c "print('hello')" shell=joojoo
@@ -33,25 +27,11 @@ Running a process in a shell
Run Keyword And Expect Error * Run Process python -c "print('hello')" shell=False
Run Keyword And Expect Error * Run Process python -c "print('hello')" shell=false
-Input things to process
- Start Process python -c "print('inp %s' % input())" shell=True stdin=PIPE
- ${process}= Get Process Object
- Log ${process.stdin.write(b"42\n")}
- Log ${process.stdin.flush()}
- ${result}= Wait For Process
- Should Match ${result.stdout} *inp 42*
-
-Assign process object to variable
- ${process} = Start Process python -c print('Hello, world!')
- ${result} = Run Process python -c import sys; print(sys.stdin.read().upper().strip()) stdin=${process.stdout}
- Wait For Process ${process}
- Should Be Equal As Strings ${result.stdout} HELLO, WORLD!
-
Get process id
${handle}= Some process
${pid}= Get Process Id ${handle}
Should Not Be Equal ${pid} ${None}
- Evaluate os.kill(int(${pid}),signal.SIGTERM) if hasattr(os, 'kill') else os.system('taskkill /pid ${pid} /f') os,signal
+ Evaluate os.kill($pid, signal.SIGTERM) if hasattr(os, 'kill') else os.system('taskkill /pid ${pid} /f')
Wait For Process ${handle}
*** Keywords ***
diff --git a/atest/testdata/standard_libraries/process/process_resource.robot b/atest/testdata/standard_libraries/process/process_resource.robot
index e2f93f1b4a5..8b312dd44d0 100644
--- a/atest/testdata/standard_libraries/process/process_resource.robot
+++ b/atest/testdata/standard_libraries/process/process_resource.robot
@@ -18,7 +18,8 @@ ${CWD} %{TEMPDIR}/process-cwd
Some process
[Arguments] ${alias}=${null} ${stderr}=STDOUT
Remove File ${STARTED}
- ${handle}= Start Python Process open(r'${STARTED}', 'w').close(); print(input())
+ ${handle}= Start Python Process
+ ... open(r'${STARTED}', 'w', encoding='ASCII').close(); print(input())
... alias=${alias} stderr=${stderr} stdin=PIPE
Wait Until Created ${STARTED} timeout=10s
Process Should Be Running
@@ -27,7 +28,7 @@ Some process
Stop some process
[Arguments] ${handle}=${NONE} ${message}=
${running}= Is Process Running ${handle}
- Return From Keyword If not $running
+ IF not $running RETURN
${process}= Get Process Object ${handle}
${stdout} ${_} = Call Method ${process} communicate ${message.encode('ASCII') + b'\n'}
RETURN ${stdout.decode('ASCII').rstrip()}
@@ -53,7 +54,7 @@ Result should match
Custom stream should contain
[Arguments] ${path} ${expected}
- Return From Keyword If not $path
+ IF not $path RETURN
${path} = Normalize Path ${path}
${content} = Get File ${path} encoding=CONSOLE
Should Be Equal ${content.rstrip()} ${expected}
@@ -65,7 +66,8 @@ Script result should equal
Result should equal ${result} ${stdout} ${stderr} ${rc}
Start Python Process
- [Arguments] ${command} ${alias}=${NONE} ${stdout}=${NONE} ${stderr}=${NONE} ${stdin}=None ${shell}=False
+ [Arguments] ${command} ${alias}=${NONE} ${stdout}=${NONE} ${stderr}=${NONE}
+ ... ${stdin}=None ${shell}=False
${handle}= Start Process python -c ${command}
... alias=${alias} stdout=${stdout} stderr=${stderr} stdin=${stdin} shell=${shell}
RETURN ${handle}
diff --git a/atest/testdata/standard_libraries/process/robot_timeouts.robot b/atest/testdata/standard_libraries/process/robot_timeouts.robot
new file mode 100644
index 00000000000..da10f909588
--- /dev/null
+++ b/atest/testdata/standard_libraries/process/robot_timeouts.robot
@@ -0,0 +1,18 @@
+*** Settings ***
+Library Process
+
+*** Test Cases ***
+Test timeout
+ [Documentation] FAIL Test timeout 500 milliseconds exceeded.
+ [Timeout] 0.5s
+ Run Process python -c import time; time.sleep(5)
+
+Keyword timeout
+ [Documentation] FAIL Keyword timeout 500 milliseconds exceeded.
+ Keyword timeout
+
+*** Keywords ***
+Keyword timeout
+ [Timeout] 0.5s
+ Start Process python -c import time; time.sleep(5)
+ Wait For Process
diff --git a/atest/testdata/standard_libraries/process/stdin.robot b/atest/testdata/standard_libraries/process/stdin.robot
index 4ba32a73591..fd9ea4669eb 100644
--- a/atest/testdata/standard_libraries/process/stdin.robot
+++ b/atest/testdata/standard_libraries/process/stdin.robot
@@ -3,12 +3,18 @@ Resource process_resource.robot
*** Test Cases ***
Stdin is NONE by default
- ${process} = Start Process python -c import sys; print('Hello, world!')
+ ${process} = Start Process python -c print('Hello, world!')
Should Be Equal ${process.stdin} ${None}
${result} = Wait For Process
Should Be Equal ${result.stdout} Hello, world!
Stdin can be set to PIPE
+ ${process} = Start Process python -c import sys; print(sys.stdin.read()) stdin=PIPE
+ Call Method ${process.stdin} write ${{b'Hello, world!'}}
+ ${result} = Wait For Process
+ Should Be Equal ${result.stdout} Hello, world!
+
+Stdin PIPE can be closed
${process} = Start Process python -c import sys; print(sys.stdin.read()) stdin=PIPE
Call Method ${process.stdin} write ${{b'Hello, world!'}}
Call Method ${process.stdin} close
@@ -43,10 +49,9 @@ Stdin as text
${result} = Run Process python -c import sys; print(sys.stdin.read()) stdin=Hyvää päivää maailma!
Should Be Equal ${result.stdout} Hyvää päivää maailma!
-Stdin as stdout from other process
- Start Process python -c print('Hello, world!')
- ${process} = Get Process Object
- ${child} = Run Process python -c import sys; print(sys.stdin.read()) stdin=${process.stdout}
- ${parent} = Wait For Process
- Should Be Equal ${child.stdout} Hello, world!\n
- Should Be Equal ${parent.stdout} ${empty}
+Stdin as stdout from another process
+ ${process} = Start Process python -c print('Hello, world!')
+ ${result1} = Run Process python -c import sys; print(sys.stdin.read().upper()) stdin=${process.stdout}
+ ${result2} = Wait For Process
+ Should Be Equal ${result1.stdout} HELLO, WORLD!\n
+ Should Be Equal ${result2.stdout} ${EMPTY}
diff --git a/atest/testdata/standard_libraries/process/stdout_and_stderr.robot b/atest/testdata/standard_libraries/process/stdout_and_stderr.robot
index e046f7f85ab..44d1b1215dc 100644
--- a/atest/testdata/standard_libraries/process/stdout_and_stderr.robot
+++ b/atest/testdata/standard_libraries/process/stdout_and_stderr.robot
@@ -109,26 +109,47 @@ Run multiple times using custom streams
Run And Test Once ${i} ${STDOUT} ${STDERR}
END
+Lot of output to stdout and stderr pipes
+ [Tags] performance
+ VAR ${code}
+ ... import sys
+ ... sys.stdout.write('Hello Robot Framework! ' * 65536)
+ ... sys.stderr.write('Hello Robot Framework! ' * 65536)
+ ... separator=;
+ ${result} = Run Process python -c ${code}
+ Length Should Be ${result.stdout} 1507328
+ Length Should Be ${result.stderr} 1507328
+ Should Be Equal ${result.rc} ${0}
+
Read standard streams when they are already closed externally
Some Process stderr=${NONE}
${stdout} = Stop Some Process message=42
Should Be Equal ${stdout} 42
${process} = Get Process Object
- Run Keyword If not ${process.stdout.closed}
- ... Call Method ${process.stdout} close
- Run Keyword If not ${process.stderr.closed}
- ... Call Method ${process.stderr} close
+ Should Be True ${process.stdout.closed}
+ Should Be True ${process.stderr.closed}
${result} = Wait For Process
- Should Be Empty ${result.stdout}${result.stderr}
+ Should Be Equal ${result.stdout} 42
+ Should Be Equal ${result.stderr} ${EMPTY}
+
+Read standard streams when they are already closed externally and only one is PIPE
+ [Documentation] Popen.communicate() behavior with closed PIPEs is strange.
+ ... https://github.com/python/cpython/issues/131064
+ ${process} = Start process python -V stderr=DEVNULL
+ Call method ${process.stdout} close
+ ${result} = Wait for process
+ Should Be Empty ${result.stdout}
+ Should Be Empty ${result.stderr}
*** Keywords ***
Run Stdout Stderr Process
[Arguments] ${stdout}=${NONE} ${stderr}=${NONE} ${cwd}=${NONE}
... ${stdout_content}=stdout ${stderr_content}=stderr
- ${code} = Catenate SEPARATOR=;
+ VAR ${code}
... import sys
... sys.stdout.write('${stdout_content}')
... sys.stderr.write('${stderr_content}')
+ ... separator=;
${result} = Run Process python -c ${code}
... stdout=${stdout} stderr=${stderr} cwd=${cwd}
RETURN ${result}
diff --git a/atest/testdata/standard_libraries/process/wait_for_process.robot b/atest/testdata/standard_libraries/process/wait_for_process.robot
index c49fbfb2175..8d75260b6d5 100644
--- a/atest/testdata/standard_libraries/process/wait_for_process.robot
+++ b/atest/testdata/standard_libraries/process/wait_for_process.robot
@@ -14,21 +14,21 @@ Wait For Process
Wait For Process Timeout
${process} = Start Python Process while True: pass
Process Should Be Running ${process}
- ${result} = Wait For Process ${process} timeout=1s
+ ${result} = Wait For Process ${process} timeout=0.25s
Process Should Be Running ${process}
Should Be Equal ${result} ${NONE}
Wait For Process Terminate On Timeout
${process} = Start Python Process while True: pass
Process Should Be Running ${process}
- ${result} = Wait For Process ${process} timeout=1s on_timeout=terminate
+ ${result} = Wait For Process ${process} timeout=0.25s on_timeout=terminate
Process Should Be Stopped ${process}
Should Not Be Equal As Integers ${result.rc} 0
Wait For Process Kill On Timeout
${process} = Start Python Process while True: pass
Process Should Be Running ${process}
- ${result} = Wait For Process ${process} timeout=1s on_timeout=kill
+ ${result} = Wait For Process ${process} timeout=0.25s on_timeout=kill
Process Should Be Stopped ${process}
Should Not Be Equal As Integers ${result.rc} 0
diff --git a/atest/testdata/standard_libraries/remote/Conflict.py b/atest/testdata/standard_libraries/remote/Conflict.py
index cdcf5a63a60..24818eb991d 100644
--- a/atest/testdata/standard_libraries/remote/Conflict.py
+++ b/atest/testdata/standard_libraries/remote/Conflict.py
@@ -1,2 +1,2 @@
def conflict():
- raise AssertionError('Should not be executed')
+ raise AssertionError("Should not be executed")
diff --git a/atest/testdata/standard_libraries/remote/argument_coersion.robot b/atest/testdata/standard_libraries/remote/argument_coersion.robot
index 2e6cc1aefc3..4fbfdb795db 100644
--- a/atest/testdata/standard_libraries/remote/argument_coersion.robot
+++ b/atest/testdata/standard_libraries/remote/argument_coersion.robot
@@ -114,15 +114,15 @@ Dictionary with non-string keys and values
{1: 'a', 2: 3, (): (), None: None} {'1': 'a', '2': 3, '()': [], '': ''}
Dictionary with non-ASCII keys
- {u'\\xe4': 1}
- {u'\\u2603': 2}
+ {'\\xe4': 1}
+ {'\\u2603': 2}
Dictionary with non-ASCII values
- {'1': u'\\xe4'}
- {'2': u'\\u2603'}
+ {'1': '\\xe4'}
+ {'2': '\\u2603'}
Dictionary with non-ASCII byte keys
- {b'\\x80': 'xx'} {'\\\\x80': 'xx'}
+ {b'\\x80': 'xx'} {'\\x80': 'xx'}
Dictionary with non-ASCII byte values
{'xx': b'\\xe4'} binary=yes
diff --git a/atest/testdata/standard_libraries/remote/arguments.py b/atest/testdata/standard_libraries/remote/arguments.py
index d5c1d71fe5f..46dd25fee16 100644
--- a/atest/testdata/standard_libraries/remote/arguments.py
+++ b/atest/testdata/standard_libraries/remote/arguments.py
@@ -1,9 +1,8 @@
import sys
-
-from datetime import datetime # Needed by `eval()`.
+from datetime import datetime # noqa: F401
from xmlrpc.client import Binary
-from remoteserver import RemoteServer, keyword
+from remoteserver import keyword, RemoteServer
class TypedRemoteServer(RemoteServer):
@@ -14,11 +13,11 @@ def _register_functions(self):
def get_keyword_types(self, name):
kw = getattr(self.library, name)
- return getattr(kw, 'robot_types', None)
+ return getattr(kw, "robot_types", None)
def get_keyword_arguments(self, name):
- if name == 'defaults_as_tuples':
- return [('first', 'eka'), ('second', 2)]
+ if name == "defaults_as_tuples":
+ return [("first", "eka"), ("second", 2)]
return RemoteServer.get_keyword_arguments(self, name)
@@ -31,26 +30,30 @@ def argument_should_be(self, argument, expected, binary=False):
self._assert_equal(argument, expected)
def _assert_equal(self, argument, expected, msg=None):
- assert argument == expected, msg or '%r != %r' % (argument, expected)
+ assert argument == expected, msg or f"{argument!r} != {expected!r}"
def _handle_binary(self, arg, required=True):
if isinstance(arg, list):
return self._handle_binary_in_list(arg)
if isinstance(arg, dict):
return self._handle_binary_in_dict(arg)
- assert isinstance(arg, Binary) or not required, 'Non-binary argument'
+ assert isinstance(arg, Binary) or not required, "Non-binary argument"
return arg.data if isinstance(arg, Binary) else arg
def _handle_binary_in_list(self, arg):
- assert any(isinstance(a, Binary) for a in arg), 'No binary in list'
+ assert any(isinstance(a, Binary) for a in arg), "No binary in list"
return [self._handle_binary(a, required=False) for a in arg]
def _handle_binary_in_dict(self, arg):
- assert any(isinstance(key, Binary) or isinstance(value, Binary)
- for key, value in arg.items()), 'No binary in dict'
- return dict((self._handle_binary(key, required=False),
- self._handle_binary(value, required=False))
- for key, value in arg.items())
+ assert any(
+ isinstance(key, Binary) or isinstance(value, Binary)
+ for key, value in arg.items()
+ ), "No binary in dict"
+ handle = self._handle_binary
+ return {
+ handle(key, required=False): handle(value, required=False)
+ for key, value in arg.items()
+ }
def kwarg_should_be(self, **kwargs):
self.argument_should_be(**kwargs)
@@ -67,17 +70,17 @@ def two_arguments(self, arg1, arg2):
def five_arguments(self, arg1, arg2, arg3, arg4, arg5):
return self._format_args(arg1, arg2, arg3, arg4, arg5)
- def arguments_with_default_values(self, arg1, arg2=2, arg3='3'):
+ def arguments_with_default_values(self, arg1, arg2=2, arg3="3"):
return self._format_args(arg1, arg2, arg3)
def varargs(self, *args):
return self._format_args(*args)
- def required_defaults_and_varargs(self, req, default='world', *varargs):
+ def required_defaults_and_varargs(self, req, default="world", *varargs):
return self._format_args(req, default, *varargs)
# Handled separately by get_keyword_arguments above.
- def defaults_as_tuples(self, first='eka', second=2):
+ def defaults_as_tuples(self, first="eka", second=2):
return self._format_args(first, second)
def kwargs(self, **kwargs):
@@ -86,40 +89,46 @@ def kwargs(self, **kwargs):
def kw_only_arg(self, *, kwo):
return self._format_args(kwo=kwo)
- def kw_only_arg_with_default(self, *, k1='default', k2):
+ def kw_only_arg_with_default(self, *, k1="default", k2):
return self._format_args(k1=k1, k2=k2)
- def args_and_kwargs(self, arg1='default1', arg2='default2', **kwargs):
+ def args_and_kwargs(self, arg1="default1", arg2="default2", **kwargs):
return self._format_args(arg1, arg2, **kwargs)
def varargs_and_kwargs(self, *varargs, **kwargs):
return self._format_args(*varargs, **kwargs)
- def all_arg_types(self, arg1, arg2='default', *varargs,
- kwo1='default', kwo2, **kwargs):
- return self._format_args(arg1, arg2, *varargs,
- kwo1=kwo1, kwo2=kwo2, **kwargs)
-
- @keyword(types=['int', '', 'dict'])
+ def all_arg_types(
+ self,
+ arg1,
+ arg2="default",
+ *varargs,
+ kwo1="default",
+ kwo2,
+ **kwargs,
+ ):
+ return self._format_args(arg1, arg2, *varargs, kwo1=kwo1, kwo2=kwo2, **kwargs)
+
+ @keyword(types=["int", "", "dict"])
def argument_types_as_list(self, integer, no_type_1, dictionary, no_type_2):
self._assert_equal(integer, 42)
- self._assert_equal(no_type_1, '42')
- self._assert_equal(dictionary, {'a': 1, 'b': 'ä'})
- self._assert_equal(no_type_2, '{}')
+ self._assert_equal(no_type_1, "42")
+ self._assert_equal(dictionary, {"a": 1, "b": "ä"})
+ self._assert_equal(no_type_2, "{}")
- @keyword(types={'integer': 'Integer', 'dictionary': 'Dictionary'})
+ @keyword(types={"integer": "Integer", "dictionary": "Dictionary"})
def argument_types_as_dict(self, integer, no_type_1, dictionary, no_type_2):
self.argument_types_as_list(integer, no_type_1, dictionary, no_type_2)
def _format_args(self, *args, **kwargs):
args = [self._format(a) for a in args]
- kwargs = [f'{k}:{self._format(kwargs[k])}' for k in sorted(kwargs)]
- return ', '.join(args + kwargs)
+ kwargs = [f"{k}:{self._format(kwargs[k])}" for k in sorted(kwargs)]
+ return ", ".join(args + kwargs)
def _format(self, arg):
type_name = type(arg).__name__
- return arg if isinstance(arg, str) else f'{arg} ({type_name})'
+ return arg if isinstance(arg, str) else f"{arg} ({type_name})"
-if __name__ == '__main__':
+if __name__ == "__main__":
TypedRemoteServer(Arguments(), *sys.argv[1:])
diff --git a/atest/testdata/standard_libraries/remote/binaryresult.py b/atest/testdata/standard_libraries/remote/binaryresult.py
index a19096f73ca..63dbb20e7cc 100644
--- a/atest/testdata/standard_libraries/remote/binaryresult.py
+++ b/atest/testdata/standard_libraries/remote/binaryresult.py
@@ -19,8 +19,8 @@ def return_binary_dict(self, **ordinals):
def return_nested_binary(self, *stuff, **more):
ret_list = [self._binary([o]) for o in stuff]
ret_dict = dict((k, self._binary([v])) for k, v in more.items())
- ret_dict['list'] = ret_list[:]
- ret_dict['dict'] = ret_dict.copy()
+ ret_dict["list"] = ret_list[:]
+ ret_dict["dict"] = ret_dict.copy()
ret_list.append(ret_dict)
return self._result(return_=ret_list)
@@ -28,17 +28,23 @@ def log_binary(self, *ordinals):
return self._result(output=self._binary(ordinals))
def fail_binary(self, *ordinals):
- return self._result(error=self._binary(ordinals, b'Error: '),
- traceback=self._binary(ordinals, b'Traceback: '))
+ return self._result(
+ error=self._binary(ordinals, b"Error: "),
+ traceback=self._binary(ordinals, b"Traceback: "),
+ )
- def _binary(self, ordinals, extra=b''):
+ def _binary(self, ordinals, extra=b""):
return Binary(extra + bytes(int(o) for o in ordinals))
- def _result(self, return_='', output='', error='', traceback=''):
- return {'status': 'PASS' if not error else 'FAIL',
- 'return': return_, 'output': output,
- 'error': error, 'traceback': traceback}
+ def _result(self, return_="", output="", error="", traceback=""):
+ return {
+ "status": "PASS" if not error else "FAIL",
+ "return": return_,
+ "output": output,
+ "error": error,
+ "traceback": traceback,
+ }
-if __name__ == '__main__':
+if __name__ == "__main__":
DirectResultRemoteServer(BinaryResult(), *sys.argv[1:])
diff --git a/atest/testdata/standard_libraries/remote/dictresult.py b/atest/testdata/standard_libraries/remote/dictresult.py
index 6bda862eea9..4554648b33d 100644
--- a/atest/testdata/standard_libraries/remote/dictresult.py
+++ b/atest/testdata/standard_libraries/remote/dictresult.py
@@ -9,11 +9,11 @@ def return_dict(self, **kwargs):
return kwargs
def return_nested_dict(self):
- return dict(key='root', nested=dict(key=42, nested=dict(key='leaf')))
+ return dict(key="root", nested=dict(key=42, nested=dict(key="leaf")))
def return_dict_in_list(self):
- return [{'foo': 1}, self.return_nested_dict()]
+ return [{"foo": 1}, self.return_nested_dict()]
-if __name__ == '__main__':
+if __name__ == "__main__":
RemoteServer(DictResult(), *sys.argv[1:])
diff --git a/atest/testdata/standard_libraries/remote/documentation.py b/atest/testdata/standard_libraries/remote/documentation.py
index 2718cf670d0..14df8a4cb96 100644
--- a/atest/testdata/standard_libraries/remote/documentation.py
+++ b/atest/testdata/standard_libraries/remote/documentation.py
@@ -7,7 +7,7 @@
class Documentation(SimpleXMLRPCServer):
def __init__(self, port=8270, port_file=None):
- SimpleXMLRPCServer.__init__(self, ('127.0.0.1', int(port)))
+ super().__init__(("127.0.0.1", int(port)))
self.register_function(self.get_keyword_names)
self.register_function(self.get_keyword_documentation)
self.register_function(self.get_keyword_arguments)
@@ -16,23 +16,27 @@ def __init__(self, port=8270, port_file=None):
self.serve_forever()
def get_keyword_names(self):
- return ['Empty', 'Single', 'Multi', 'Nön-ÄSCII']
+ return ["Empty", "Single", "Multi", "Nön-ÄSCII"]
def get_keyword_documentation(self, name):
- return {'__intro__': 'Remote library for documentation testing purposes',
- 'Empty': '',
- 'Single': 'Single line documentation',
- 'Multi': 'Short doc\nin two lines.\n\nDoc body\nin\nthree.',
- 'Nön-ÄSCII': 'Nön-ÄSCII documentation'}.get(name)
+ return {
+ "__intro__": "Remote library for documentation testing purposes",
+ "Empty": "",
+ "Single": "Single line documentation",
+ "Multi": "Short doc\nin two lines.\n\nDoc body\nin\nthree.",
+ "Nön-ÄSCII": "Nön-ÄSCII documentation",
+ }.get(name)
def get_keyword_arguments(self, name):
- return {'Empty': (),
- 'Single': ['arg'],
- 'Multi': ['a1', 'a2=d', '*varargs']}.get(name)
+ return {
+ "Empty": (),
+ "Single": ["arg"],
+ "Multi": ["a1", "a2=d", "*varargs"],
+ }.get(name)
def run_keyword(self, name, args):
- return {'status': 'PASS'}
+ return {"status": "PASS"}
-if __name__ == '__main__':
+if __name__ == "__main__":
Documentation(*sys.argv[1:])
diff --git a/atest/testdata/standard_libraries/remote/invalid.py b/atest/testdata/standard_libraries/remote/invalid.py
index edecdc75048..c6e8cb95107 100644
--- a/atest/testdata/standard_libraries/remote/invalid.py
+++ b/atest/testdata/standard_libraries/remote/invalid.py
@@ -1,4 +1,5 @@
import sys
+
from remoteserver import DirectResultRemoteServer
@@ -11,7 +12,7 @@ def invalid_result_dict(self):
return {}
def invalid_char_in_xml(self):
- return {'status': 'PASS', 'return': '\x00'}
+ return {"status": "PASS", "return": "\x00"}
def exception(self, message):
raise Exception(message)
@@ -20,5 +21,5 @@ def shutdown(self):
sys.exit()
-if __name__ == '__main__':
+if __name__ == "__main__":
DirectResultRemoteServer(Invalid(), *sys.argv[1:])
diff --git a/atest/testdata/standard_libraries/remote/keywordtags.py b/atest/testdata/standard_libraries/remote/keywordtags.py
index f5425e4a49f..4cb752abdda 100644
--- a/atest/testdata/standard_libraries/remote/keywordtags.py
+++ b/atest/testdata/standard_libraries/remote/keywordtags.py
@@ -1,6 +1,6 @@
import sys
-from remoteserver import RemoteServer, keyword
+from remoteserver import keyword, RemoteServer
class KeywordTags:
@@ -23,14 +23,14 @@ def doc_contains_tags_after_doc(self):
def empty_robot_tags_means_no_tags(self):
pass
- @keyword(tags=['foo', 'bar', 'FOO', '42'])
+ @keyword(tags=["foo", "bar", "FOO", "42"])
def robot_tags(self):
pass
- @keyword(tags=['foo', 'bar'])
+ @keyword(tags=["foo", "bar"])
def robot_tags_and_doc_tags(self):
"""Tags: bar, zap"""
-if __name__ == '__main__':
+if __name__ == "__main__":
RemoteServer(KeywordTags(), *sys.argv[1:])
diff --git a/atest/testdata/standard_libraries/remote/libraryinfo.py b/atest/testdata/standard_libraries/remote/libraryinfo.py
index efbd66388ea..be2f86bbce4 100644
--- a/atest/testdata/standard_libraries/remote/libraryinfo.py
+++ b/atest/testdata/standard_libraries/remote/libraryinfo.py
@@ -1,27 +1,27 @@
import sys
-from remoteserver import RemoteServer, keyword
+from remoteserver import RemoteServer
class BulkLoadRemoteServer(RemoteServer):
def _register_functions(self):
- """
- Individual get_keyword_* methods are not registered.
- This removes the fall back scenario should get_library_information fail.
- """
+ # Individual get_keyword_* methods are not registered.
+ # This removes the fallback scenario should get_library_information fail.
self.register_function(self.get_library_information)
self.register_function(self.run_keyword)
def get_library_information(self):
- info_dict = {'__init__': {'doc': '__init__ documentation.'},
- '__intro__': {'doc': '__intro__ documentation.'}}
+ info_dict = {
+ "__init__": {"doc": "__init__ documentation."},
+ "__intro__": {"doc": "__intro__ documentation."},
+ }
for kw in self.get_keyword_names():
info_dict[kw] = dict(
- args=['arg', '*extra'] if kw == 'some_keyword' else ['arg=None'],
- doc="Documentation for '%s'." % kw,
- tags=['tag'],
- types=['bool'] if kw == 'some_keyword' else ['int']
+ args=["arg", "*extra"] if kw == "some_keyword" else ["arg=None"],
+ doc=f"Documentation for '{kw}'.",
+ tags=["tag"],
+ types=["bool"] if kw == "some_keyword" else ["int"],
)
return info_dict
@@ -30,11 +30,11 @@ class The10001KeywordsLibrary:
def __init__(self):
for i in range(10000):
- setattr(self, 'keyword_%d' % i, lambda result=str(i): result)
+ setattr(self, f"keyword_{i}", lambda result=str(i): result)
def some_keyword(self, arg, *extra):
- return 'yes' if arg else 'no'
+ return "yes" if arg else "no"
-if __name__ == '__main__':
+if __name__ == "__main__":
BulkLoadRemoteServer(The10001KeywordsLibrary(), *sys.argv[1:])
diff --git a/atest/testdata/standard_libraries/remote/remoteserver.py b/atest/testdata/standard_libraries/remote/remoteserver.py
index b3192275905..3853061cdc9 100644
--- a/atest/testdata/standard_libraries/remote/remoteserver.py
+++ b/atest/testdata/standard_libraries/remote/remoteserver.py
@@ -8,18 +8,20 @@
def keyword(name=None, tags=(), types=()):
if callable(name):
return keyword()(name)
+
def deco(func):
func.robot_name = name
func.robot_tags = tags
func.robot_types = types
return func
+
return deco
class RemoteServer(SimpleXMLRPCServer):
def __init__(self, library, port=8270, port_file=None):
- SimpleXMLRPCServer.__init__(self, ('127.0.0.1', int(port)))
+ super().__init__(("127.0.0.1", int(port)))
self.library = library
self._shutdown = False
self._register_functions()
@@ -38,47 +40,47 @@ def serve_forever(self):
self.handle_request()
def get_keyword_names(self):
- return [attr for attr in dir(self.library) if attr[0] != '_']
+ return [attr for attr in dir(self.library) if attr[0] != "_"]
def get_keyword_arguments(self, name):
kw = getattr(self.library, name)
- args, varargs, kwargs, defaults, kwoargs, kwodefaults, _ \
- = inspect.getfullargspec(kw)
+ args, varargs, kwargs, defaults, kwoargs, kwodefaults, _ = (
+ inspect.getfullargspec(kw)
+ )
args = args[1:] # drop 'self'
if defaults:
- args, names = args[:-len(defaults)], args[-len(defaults):]
- args += [f'{n}={d}' for n, d in zip(names, defaults)]
+ args, names = args[: -len(defaults)], args[-len(defaults) :]
+ args += [f"{n}={d}" for n, d in zip(names, defaults)]
if varargs:
- args.append(f'*{varargs}')
+ args.append(f"*{varargs}")
if kwoargs:
if not varargs:
- args.append('*')
+ args.append("*")
args += [self._format_kwo(arg, kwodefaults) for arg in kwoargs]
if kwargs:
- args.append(f'**{kwargs}')
+ args.append(f"**{kwargs}")
return args
def _format_kwo(self, arg, defaults):
if defaults and arg in defaults:
- return f'{arg}={defaults[arg]}'
+ return f"{arg}={defaults[arg]}"
return arg
def get_keyword_tags(self, name):
kw = getattr(self.library, name)
- return getattr(kw, 'robot_tags', [])
+ return getattr(kw, "robot_tags", [])
def get_keyword_documentation(self, name):
kw = getattr(self.library, name)
- return inspect.getdoc(kw) or ''
+ return inspect.getdoc(kw) or ""
def run_keyword(self, name, args, kwargs=None):
try:
result = getattr(self.library, name)(*args, **(kwargs or {}))
except AssertionError as err:
- return {'status': 'FAIL', 'error': str(err)}
+ return {"status": "FAIL", "error": str(err)}
else:
- return {'status': 'PASS',
- 'return': result if result is not None else ''}
+ return {"status": "PASS", "return": result if result is not None else ""}
class DirectResultRemoteServer(RemoteServer):
@@ -88,13 +90,13 @@ def run_keyword(self, name, args, kwargs=None):
return getattr(self.library, name)(*args, **(kwargs or {}))
except SystemExit:
self._shutdown = True
- return {'status': 'PASS'}
+ return {"status": "PASS"}
def announce_port(socket, port_file=None):
port = socket.getsockname()[1]
- sys.stdout.write(f'Remote server starting on port {port}.\n')
+ sys.stdout.write(f"Remote server starting on port {port}.\n")
sys.stdout.flush()
if port_file:
- with open(port_file, 'w') as f:
+ with open(port_file, "w", encoding="ASCII") as f:
f.write(str(port))
diff --git a/atest/testdata/standard_libraries/remote/returnvalues.py b/atest/testdata/standard_libraries/remote/returnvalues.py
index 229992dcfb8..03348dcaea0 100644
--- a/atest/testdata/standard_libraries/remote/returnvalues.py
+++ b/atest/testdata/standard_libraries/remote/returnvalues.py
@@ -7,7 +7,7 @@
class ReturnValues:
def string(self):
- return 'Hyvä tulos!'
+ return "Hyvä tulos!"
def integer(self):
return 42
@@ -22,11 +22,11 @@ def datetime(self):
return datetime.datetime(2023, 9, 14, 17, 30, 23)
def list(self):
- return [1, 2, 'lolme']
+ return [1, 2, "lolme"]
def dict(self):
- return {'a': 1, 'b': [2, 3]}
+ return {"a": 1, "b": [2, 3]}
-if __name__ == '__main__':
+if __name__ == "__main__":
RemoteServer(ReturnValues(), *sys.argv[1:])
diff --git a/atest/testdata/standard_libraries/remote/simpleserver.py b/atest/testdata/standard_libraries/remote/simpleserver.py
index aea824e073a..7c4bb58918a 100644
--- a/atest/testdata/standard_libraries/remote/simpleserver.py
+++ b/atest/testdata/standard_libraries/remote/simpleserver.py
@@ -7,35 +7,42 @@
class SimpleServer(SimpleXMLRPCServer):
def __init__(self, port=8270, port_file=None):
- SimpleXMLRPCServer.__init__(self, ('127.0.0.1', int(port)))
+ super().__init__(("127.0.0.1", int(port)))
self.register_function(self.get_keyword_names)
self.register_function(self.run_keyword)
announce_port(self.socket, port_file)
self.serve_forever()
def get_keyword_names(self):
- return ['Passing', 'Failing', 'Traceback', 'Returning', 'Logging',
- 'Extra stuff in result dictionary',
- 'Conflict', 'Should Be True']
+ return [
+ "Passing",
+ "Failing",
+ "Traceback",
+ "Returning",
+ "Logging",
+ "Extra stuff in result dictionary",
+ "Conflict",
+ "Should Be True",
+ ]
def run_keyword(self, name, args):
- if name == 'Passing':
- return {'status': 'PASS'}
- if name == 'Failing':
- return {'status': 'FAIL', 'error': ' '.join(args)}
- if name == 'Traceback':
- return {'status': 'FAIL', 'traceback': ' '.join(args)}
- if name == 'Returning':
- return {'status': 'PASS', 'return': ' '.join(args)}
- if name == 'Logging':
- return {'status': 'PASS', 'output': '\n'.join(args)}
- if name == 'Extra stuff in result dictionary':
- return {'status': 'PASS', 'extra': 'stuff', 'is': 'ignored'}
- if name == 'Conflict':
- return {'status': 'FAIL', 'error': 'Should not be executed'}
- if name == 'Should Be True':
- return {'status': 'PASS', 'output': 'Always passes'}
-
-
-if __name__ == '__main__':
+ if name == "Passing":
+ return {"status": "PASS"}
+ if name == "Failing":
+ return {"status": "FAIL", "error": " ".join(args)}
+ if name == "Traceback":
+ return {"status": "FAIL", "traceback": " ".join(args)}
+ if name == "Returning":
+ return {"status": "PASS", "return": " ".join(args)}
+ if name == "Logging":
+ return {"status": "PASS", "output": "\n".join(args)}
+ if name == "Extra stuff in result dictionary":
+ return {"status": "PASS", "extra": "stuff", "is": "ignored"}
+ if name == "Conflict":
+ return {"status": "FAIL", "error": "Should not be executed"}
+ if name == "Should Be True":
+ return {"status": "PASS", "output": "Always passes"}
+
+
+if __name__ == "__main__":
SimpleServer(*sys.argv[1:])
diff --git a/atest/testdata/standard_libraries/remote/specialerrors.py b/atest/testdata/standard_libraries/remote/specialerrors.py
index 2105da628b3..7dadfe31db1 100644
--- a/atest/testdata/standard_libraries/remote/specialerrors.py
+++ b/atest/testdata/standard_libraries/remote/specialerrors.py
@@ -1,4 +1,5 @@
import sys
+
from remoteserver import DirectResultRemoteServer
@@ -8,13 +9,19 @@ def continuable(self, message, traceback):
return self._special_error(message, traceback, continuable=True)
def fatal(self, message, traceback):
- return self._special_error(message, traceback,
- fatal='this wins', continuable=42)
+ return self._special_error(
+ message, traceback, fatal="this wins", continuable=42
+ )
def _special_error(self, message, traceback, continuable=False, fatal=False):
- return {'status': 'FAIL', 'error': message, 'traceback': traceback,
- 'continuable': continuable, 'fatal': fatal}
+ return {
+ "status": "FAIL",
+ "error": message,
+ "traceback": traceback,
+ "continuable": continuable,
+ "fatal": fatal,
+ }
-if __name__ == '__main__':
+if __name__ == "__main__":
DirectResultRemoteServer(SpecialErrors(), *sys.argv[1:])
diff --git a/atest/testdata/standard_libraries/remote/timeouts.py b/atest/testdata/standard_libraries/remote/timeouts.py
index 04d39a13cf7..0ab67164c9f 100644
--- a/atest/testdata/standard_libraries/remote/timeouts.py
+++ b/atest/testdata/standard_libraries/remote/timeouts.py
@@ -1,5 +1,6 @@
import sys
import time
+
from remoteserver import RemoteServer
@@ -9,5 +10,5 @@ def sleep(self, secs):
time.sleep(int(secs))
-if __name__ == '__main__':
+if __name__ == "__main__":
RemoteServer(Timeouts(), *sys.argv[1:])
diff --git a/atest/testdata/standard_libraries/remote/variables.py b/atest/testdata/standard_libraries/remote/variables.py
index d7cb6b7326d..7b658e75229 100644
--- a/atest/testdata/standard_libraries/remote/variables.py
+++ b/atest/testdata/standard_libraries/remote/variables.py
@@ -3,7 +3,7 @@
class MyObject:
- def __init__(self, name=''):
+ def __init__(self, name=""):
self.name = name
def __str__(self):
diff --git a/atest/testdata/standard_libraries/screenshot/take_screenshot.robot b/atest/testdata/standard_libraries/screenshot/take_screenshot.robot
index 470acbf5ff2..5fc43347b77 100644
--- a/atest/testdata/standard_libraries/screenshot/take_screenshot.robot
+++ b/atest/testdata/standard_libraries/screenshot/take_screenshot.robot
@@ -24,7 +24,7 @@ Basename May Be Defined
Basename With Extension Turns Off Index Generation
Repeat Keyword 3 Take Screenshot xxx.jpg
Repeat Keyword 2 Take Screenshot yyy.jpeg
- Screenshots Should Exist ${OUTPUTDIR} xxx.jpg yyy.jpeg
+ Screenshots Should Exist ${OUTPUTDIR} xxx.jpg yyy.jpeg
Name as `pathlib.Path`
Take Screenshot ${{pathlib.Path('name.jpg')}}
@@ -35,7 +35,7 @@ Screenshot Width Can Be Given
Screenshots Should Exist ${OUTPUTDIR} ${FIRST_SCREENSHOT}
Basename With Non-existing Directories Fails
- [Documentation] FAIL Directory '${OUTPUTDIR}${/}non-existing' where to save the screenshot does not exist
+ [Documentation] FAIL Directory '${OUTPUTDIR}${/}non-existing' where to save the screenshot does not exist.
Take Screenshot ${OUTPUTDIR}${/}non-existing${/}foo
Without Embedding
diff --git a/atest/testdata/standard_libraries/string/format_string.robot b/atest/testdata/standard_libraries/string/format_string.robot
index 15ed854ea40..d40ac487618 100644
--- a/atest/testdata/standard_libraries/string/format_string.robot
+++ b/atest/testdata/standard_libraries/string/format_string.robot
@@ -33,6 +33,16 @@ Format String From Non-ASCII Template
${result} = Format String {} and {} are fruits from Brazil Açaí Cupuaçu
Should be equal ${result} Açaí and Cupuaçu are fruits from Brazil
+Template can contain '=' without escaping
+ ${result} = Format String x={}, y={} 1 2
+ Should be equal ${result} x=1, y=2
+ ${result} = Format String x={x}, y={y} y=2 x=1
+ Should be equal ${result} x=1, y=2
+ ${result} = Format String template={} always positional
+ Should be equal ${result} template=always positional
+ ${result} = Format String escaping\={} ok as well
+ Should be equal ${result} escaping=ok as well
+
Format String From Template File
${result} = Format String ${CURDIR}/format_string_template.txt condition=supports
Should be equal ${result} Format String also supports files templates!
diff --git a/atest/testdata/standard_libraries/string/string.robot b/atest/testdata/standard_libraries/string/string.robot
index b184ae3c7d7..f28023e4a7f 100644
--- a/atest/testdata/standard_libraries/string/string.robot
+++ b/atest/testdata/standard_libraries/string/string.robot
@@ -35,6 +35,11 @@ Split To Lines
Length Should Be ${result} 2
Should be equal ${result}[0] ${FIRST LINE}
Should be equal ${result}[1] ${SECOND LINE}
+ @{result} = Split To Lines Just one line!
+ Length Should Be ${result} 1
+ Should be equal ${result}[0] Just one line!
+ @{result} = Split To Lines ${EMPTY}
+ Length Should Be ${result} 0
Split To Lines With Start Only
@{result} = Split To Lines ${TEXT IN COLUMNS} 1
diff --git a/atest/testdata/standard_libraries/telnet/telnet_variables.py b/atest/testdata/standard_libraries/telnet/telnet_variables.py
index 6c60d6a1e85..85d32edf8b2 100644
--- a/atest/testdata/standard_libraries/telnet/telnet_variables.py
+++ b/atest/testdata/standard_libraries/telnet/telnet_variables.py
@@ -1,10 +1,10 @@
import platform
# We assume that prompt is PS1='\u@\h \W \$ '
-HOST = 'localhost'
-USERNAME = 'test'
-PASSWORD = 'test'
-PROMPT = '$ '
-FULL_PROMPT = '%s@%s ~ $ ' % (USERNAME, platform.uname()[1])
-PROMPT_START = '%s@' % USERNAME
-HOME = '/home/%s' % USERNAME
+HOST = "localhost"
+USERNAME = "test"
+PASSWORD = "test"
+PROMPT = "$ "
+FULL_PROMPT = f"{USERNAME}@{platform.uname()[1]} ~ $ "
+PROMPT_START = f"{USERNAME}@"
+HOME = f"/home/{USERNAME}"
diff --git a/atest/testdata/tags/-tag_syntax.robot b/atest/testdata/tags/-tag_syntax.robot
index 18f80ae06b6..c0bbf816573 100644
--- a/atest/testdata/tags/-tag_syntax.robot
+++ b/atest/testdata/tags/-tag_syntax.robot
@@ -1,5 +1,5 @@
*** Settings ***
-Test Tags -in-settings tag1 tag2 tag3 ${TAG}
+Test Tags -in-settings tag1 tag2 tag3 ${TAG} \-escaped-in-settings
Keyword Tags -in-settings kw1 kw2
Resource -tag_syntax.resource
@@ -9,11 +9,11 @@ ${VAR} -variable
*** Test Cases ***
Remove from test
- [Tags] -tag2 tag4 -${tag} --in-settings
+ [Tags] -tag2 tag4 -${tag} --in-settings -\-escaped-in-settings
Remove from keyword
Remove from test using pattern
- [Tags] -tag[12]
+ [Tags] -tag[12] -*esc*
Remove from keyword using pattern
Escaped
diff --git a/atest/testdata/tags/include_and_exclude.robot b/atest/testdata/tags/include_and_exclude.robot
index c9602ba167e..1fa8fd460a7 100644
--- a/atest/testdata/tags/include_and_exclude.robot
+++ b/atest/testdata/tags/include_and_exclude.robot
@@ -1,5 +1,5 @@
*** Settings ***
-Force Tags force robot:just-an-example ROBOT : XXX
+Test Tags force robot:just-an-example ROBOT : XXX
*** Test Cases ***
Incl-1
@@ -26,6 +26,10 @@ Excl-123
[Tags] excl_1 excl_2 excl_3
No Operation
-Robot-exclude
- [Tags] robot:exclude ROBOT:EXCLUDE
- Fail This test will never be run
+robot:exclude
+ [Tags] robot:exclude
+ Fail This test will never be run
+
+robot:exclude using variable
+ [Tags] ROBOT${{':'}}EXCLUDE
+ Fail This test will never be run
diff --git a/atest/testdata/test_libraries/AvoidProperties.py b/atest/testdata/test_libraries/AvoidProperties.py
index 2cde4ec4b36..c19073f9676 100644
--- a/atest/testdata/test_libraries/AvoidProperties.py
+++ b/atest/testdata/test_libraries/AvoidProperties.py
@@ -19,13 +19,13 @@ def __set__(self, instance, value):
class FailingNonDataDescriptor(NonDataDescriptor):
def __get__(self, instance, owner):
- return 1/0
+ return 1 / 0
class FailingDataDescriptor(DataDescriptor):
def __get__(self, instance, owner):
- return 1/0
+ return 1 / 0
class AvoidProperties:
@@ -95,4 +95,3 @@ def failing_data_descriptor(self):
@FailingDataDescriptor
def failing_classmethod_data_descriptor(self):
pass
-
diff --git a/atest/testdata/test_libraries/ClassWithAutoKeywordsOff.py b/atest/testdata/test_libraries/ClassWithAutoKeywordsOff.py
index 7840f74cc6d..1dbf196f18f 100644
--- a/atest/testdata/test_libraries/ClassWithAutoKeywordsOff.py
+++ b/atest/testdata/test_libraries/ClassWithAutoKeywordsOff.py
@@ -4,8 +4,8 @@
class InvalidGetattr:
def __getattr__(self, item):
- if item == 'robot_name':
- raise ValueError('This goes through getattr() and hasattr().')
+ if item == "robot_name":
+ raise ValueError("This goes through getattr() and hasattr().")
raise AttributeError
@@ -13,17 +13,17 @@ class ClassWithAutoKeywordsOff:
ROBOT_AUTO_KEYWORDS = False
def public_method_is_not_keyword(self):
- raise RuntimeError('Should not be executed!')
+ raise RuntimeError("Should not be executed!")
@keyword(name="Decorated Method Is Keyword")
def decorated_method(self):
- print('Decorated methods are keywords.')
+ print("Decorated methods are keywords.")
def _private_method_is_not_keyword(self):
- raise RuntimeError('Should not be executed!')
+ raise RuntimeError("Should not be executed!")
@keyword
def _private_decorated_method_is_keyword(self):
- print('Decorated private methods are keywords.')
+ print("Decorated private methods are keywords.")
invalid_getattr = InvalidGetattr()
diff --git a/atest/testdata/test_libraries/CustomDir.py b/atest/testdata/test_libraries/CustomDir.py
new file mode 100644
index 00000000000..9b3fa06afac
--- /dev/null
+++ b/atest/testdata/test_libraries/CustomDir.py
@@ -0,0 +1,24 @@
+from robot.api.deco import keyword, library
+
+
+@library
+class CustomDir:
+
+ def __dir__(self):
+ return ["normal", "via_getattr", "via_getattr_invalid", "non_existing"]
+
+ @keyword
+ def normal(self, arg):
+ print(arg.upper())
+
+ def __getattr__(self, name):
+ if name == "via_getattr":
+
+ @keyword
+ def func(arg):
+ print(arg.upper())
+
+ return func
+ if name == "via_getattr_invalid":
+ raise ValueError("This is invalid!")
+ raise AttributeError(f"{name!r} does not exist.")
diff --git a/atest/testdata/test_libraries/DynamicLibraryTags.py b/atest/testdata/test_libraries/DynamicLibraryTags.py
index 1d88fdc2f8c..abf0bc5eee2 100644
--- a/atest/testdata/test_libraries/DynamicLibraryTags.py
+++ b/atest/testdata/test_libraries/DynamicLibraryTags.py
@@ -1,8 +1,11 @@
KWS = {
- 'Only tags in documentation': ('Tags: tag1, tag2', None),
- 'Tags in addition to normal documentation': ('Normal doc\n\n...\n\nTags: tag', None),
- 'Tags from get_keyword_tags': (None, ['t1', 't2', 't3']),
- 'Tags both from doc and get_keyword_tags': ('Tags: 1, 2', ['4', '2', '3'])
+ "Only tags in documentation": ("Tags: tag1, tag2", None),
+ "Tags in addition to normal documentation": (
+ "Normal doc\n\n...\n\nTags: tag",
+ None,
+ ),
+ "Tags from get_keyword_tags": (None, ["t1", "t2", "t3"]),
+ "Tags both from doc and get_keyword_tags": ("Tags: 1, 2", ["4", "2", "3"]),
}
@@ -17,8 +20,9 @@ def run_keyword(self, name, args, kwags):
def get_keyword_documentation(self, name):
if not self.get_keyword_tags_called:
- raise AssertionError("'get_keyword_tags' should be called before "
- "'get_keyword_documentation'")
+ raise AssertionError(
+ "'get_keyword_tags' should be called before 'get_keyword_documentation'"
+ )
return KWS[name][0]
def get_keyword_tags(self, name):
diff --git a/atest/testdata/test_libraries/Embedded.py b/atest/testdata/test_libraries/Embedded.py
index 98530b98fc4..2b9230c31c4 100644
--- a/atest/testdata/test_libraries/Embedded.py
+++ b/atest/testdata/test_libraries/Embedded.py
@@ -6,9 +6,10 @@ class Embedded:
def __init__(self):
self.called = 0
- @keyword('Called ${times} time(s)', types={'times': int})
+ @keyword("Called ${times} time(s)", types={"times": int})
def called_times(self, times):
self.called += 1
if self.called != times:
- raise AssertionError('Called %s time(s), expected %s time(s).'
- % (self.called, times))
+ raise AssertionError(
+ f"Called {self.called} time(s), expected {times} time(s)."
+ )
diff --git a/atest/testdata/test_libraries/HybridWithNotKeywordDecorator.py b/atest/testdata/test_libraries/HybridWithNotKeywordDecorator.py
index f1152464a10..db22bd05ed9 100644
--- a/atest/testdata/test_libraries/HybridWithNotKeywordDecorator.py
+++ b/atest/testdata/test_libraries/HybridWithNotKeywordDecorator.py
@@ -4,7 +4,7 @@
class HybridWithNotKeywordDecorator:
def get_keyword_names(self):
- return ['exposed_in_hybrid', 'not_exposed_in_hybrid']
+ return ["exposed_in_hybrid", "not_exposed_in_hybrid"]
def exposed_in_hybrid(self):
pass
diff --git a/atest/testdata/test_libraries/ImportLogging.py b/atest/testdata/test_libraries/ImportLogging.py
index 7fb7a944e2b..e7b45257127 100644
--- a/atest/testdata/test_libraries/ImportLogging.py
+++ b/atest/testdata/test_libraries/ImportLogging.py
@@ -1,10 +1,10 @@
import sys
-from robot.api import logger
+from robot.api import logger
-print('*WARN* Warning via stdout in import')
-print('Info via stderr in import', file=sys.stderr)
-logger.warn('Warning via API in import')
+print("*WARN* Warning via stdout in import")
+print("Info via stderr in import", file=sys.stderr)
+logger.warn("Warning via API in import")
def keyword():
diff --git a/atest/testdata/test_libraries/ImportRobotModuleTestLibrary.py b/atest/testdata/test_libraries/ImportRobotModuleTestLibrary.py
index 2ca568e3a00..22c21ecf47a 100644
--- a/atest/testdata/test_libraries/ImportRobotModuleTestLibrary.py
+++ b/atest/testdata/test_libraries/ImportRobotModuleTestLibrary.py
@@ -1,6 +1,6 @@
def importing_robot_module_directly_fails():
try:
- import running
+ import running # noqa: F401
except ImportError:
pass
else:
@@ -8,17 +8,19 @@ def importing_robot_module_directly_fails():
def importing_robot_module_through_robot_succeeds():
- from robot import running
+ from robot import running # noqa: F401
def importing_standard_library_directly_fails():
try:
- import BuiltIn
+ import BuiltIn # noqa: F401
except ImportError:
pass
else:
raise AssertionError("Importing 'BuiltIn' directly succeeded!")
+
def importing_standard_library_through_robot_libraries_succeeds():
from robot.libraries import BuiltIn
- BuiltIn.BuiltIn().set_test_variable('${SET BY LIBRARY}', 42)
+
+ BuiltIn.BuiltIn().set_test_variable("${SET BY LIBRARY}", 42)
diff --git a/atest/testdata/test_libraries/InitImportingAndIniting.py b/atest/testdata/test_libraries/InitImportingAndIniting.py
index f278c13da8e..ddb7e82022a 100644
--- a/atest/testdata/test_libraries/InitImportingAndIniting.py
+++ b/atest/testdata/test_libraries/InitImportingAndIniting.py
@@ -1,24 +1,23 @@
-from robot.libraries.BuiltIn import BuiltIn
from robot.api import logger
+from robot.libraries.BuiltIn import BuiltIn
class Importing:
def __init__(self):
- BuiltIn().import_library('String')
+ BuiltIn().import_library("String")
def kw_from_lib_with_importing_init(self):
- print('Keyword from library with importing __init__.')
+ print("Keyword from library with importing __init__.")
class Initting:
def __init__(self):
- # This initializes the accesses library.
- self.lib = BuiltIn().get_library_instance('InitImportingAndIniting.Initted')
+ self.lib = BuiltIn().get_library_instance("InitImportingAndIniting.Initted")
def kw_from_lib_with_initting_init(self):
- logger.info('Keyword from library with initting __init__.')
+ logger.info("Keyword from library with initting __init__.")
self.lib.kw_from_lib_initted_by_init()
@@ -28,4 +27,4 @@ def __init__(self, id):
self.id = id
def kw_from_lib_initted_by_init(self):
- print('Keyword from library initted by __init__ (id: %s).' % self.id)
+ print(f"Keyword from library initted by __init__ (id: {self.id}).")
diff --git a/atest/testdata/test_libraries/InitLogging.py b/atest/testdata/test_libraries/InitLogging.py
index cd527063f4d..417eb4f8eac 100644
--- a/atest/testdata/test_libraries/InitLogging.py
+++ b/atest/testdata/test_libraries/InitLogging.py
@@ -1,4 +1,5 @@
import sys
+
from robot.api import logger
@@ -7,10 +8,9 @@ class InitLogging:
def __init__(self):
InitLogging.called += 1
- print('*WARN* Warning via stdout in init', self.called)
- print('Info via stderr in init', self.called, file=sys.stderr)
- logger.warn('Warning via API in init %d' % self.called)
+ print("*WARN* Warning via stdout in init", self.called)
+ print("Info via stderr in init", self.called, file=sys.stderr)
+ logger.warn(f"Warning via API in init {self.called}")
def keyword(self):
pass
-
diff --git a/atest/testdata/test_libraries/InitializationFailLibrary.py b/atest/testdata/test_libraries/InitializationFailLibrary.py
index 24c680f68ad..2c5524918cd 100644
--- a/atest/testdata/test_libraries/InitializationFailLibrary.py
+++ b/atest/testdata/test_libraries/InitializationFailLibrary.py
@@ -1,4 +1,4 @@
class InitializationFailLibrary:
- def __init__(self, arg1='default 1', arg2='default 2'):
- raise Exception("Initialization failed with arguments %r and %r!" % (arg1, arg2))
+ def __init__(self, arg1="default 1", arg2="default 2"):
+ raise Exception(f"Initialization failed with arguments {arg1!r} and {arg2!r}!")
diff --git a/atest/testdata/test_libraries/LibUsingLoggingApi.py b/atest/testdata/test_libraries/LibUsingLoggingApi.py
index cc8b17affec..9191717abb0 100644
--- a/atest/testdata/test_libraries/LibUsingLoggingApi.py
+++ b/atest/testdata/test_libraries/LibUsingLoggingApi.py
@@ -1,12 +1,13 @@
import time
+
from robot.api import logger
def log_with_all_levels():
- for level in 'trace debug info warn error'.split():
- msg = '%s msg' % level
- logger.write(msg+' 1', level)
- getattr(logger, level)(msg+' 2', html=False)
+ for level in "trace debug info warn error".split():
+ msg = f"{level} msg"
+ logger.write(msg + " 1", level)
+ getattr(logger, level)(msg + " 2", html=False)
def write(message, level):
@@ -14,27 +15,28 @@ def write(message, level):
def log_messages_different_time():
- logger.info('First message')
+ logger.info("First message")
time.sleep(0.1)
- logger.info('Second message 0.1 sec later')
+ logger.info("Second message 0.1 sec later")
def log_html():
- logger.write('debug', level='DEBUG', html=True)
- logger.info('info', html=True)
- logger.warn('warn', html=True)
+ logger.write("debug", level="DEBUG", html=True)
+ logger.info("info", html=True)
+ logger.warn("warn", html=True)
def write_messages_to_console():
- logger.console('To console only')
- logger.console('To console ', newline=False)
- logger.console('in two parts')
- logger.info('To log and console', also_console=True)
+ logger.console("To console only")
+ logger.console("To console ", newline=False)
+ logger.console("in two parts")
+ logger.info("To log and console", also_console=True)
def log_non_strings():
logger.info(42)
- logger.warn(True)
+ logger.warn(True, html=True)
+ logger.info(None)
def log_callable():
diff --git a/atest/testdata/test_libraries/LibUsingPyLogging.py b/atest/testdata/test_libraries/LibUsingPyLogging.py
index 5bdc940b753..c14588c99dc 100644
--- a/atest/testdata/test_libraries/LibUsingPyLogging.py
+++ b/atest/testdata/test_libraries/LibUsingPyLogging.py
@@ -1,24 +1,24 @@
import logging
-import time
import sys
+import time
class CustomHandler(logging.Handler):
def emit(self, record):
- sys.__stdout__.write(record.getMessage().title() + '\n')
+ sys.__stdout__.write(record.getMessage().title() + "\n")
-custom = logging.getLogger('custom')
+custom = logging.getLogger("custom")
custom.addHandler(CustomHandler())
-nonprop = logging.getLogger('nonprop')
+nonprop = logging.getLogger("nonprop")
nonprop.propagate = False
nonprop.addHandler(CustomHandler())
class Message:
- def __init__(self, msg=''):
+ def __init__(self, msg=""):
self.msg = msg
def __str__(self):
@@ -31,31 +31,31 @@ def __repr__(self):
class InvalidMessage(Message):
def __str__(self):
- raise AssertionError('Should not have been logged')
+ raise AssertionError("Should not have been logged")
def log_with_default_levels():
- logging.debug('debug message')
- logging.info('%s %s', 'info', 'message')
- logging.warning(Message('warning message'))
- logging.error('error message')
- #critical is considered a warning
- logging.critical('critical message')
+ logging.debug("debug message")
+ logging.info("%s %s", "info", "message")
+ logging.warning(Message("warning message"))
+ logging.error("error message")
+ # critical is considered a warning
+ logging.critical("critical message")
def log_with_custom_levels():
- logging.log(logging.DEBUG-1, Message('below debug'))
- logging.log(logging.INFO-1, 'between debug and info')
- logging.log(logging.INFO+1, 'between info and warning')
- logging.log(logging.WARNING+5, 'between warning and error')
- logging.log(logging.ERROR*100,'above error')
+ logging.log(logging.DEBUG - 1, Message("below debug"))
+ logging.log(logging.INFO - 1, "between debug and info")
+ logging.log(logging.INFO + 1, "between info and warning")
+ logging.log(logging.WARNING + 5, "between warning and error")
+ logging.log(logging.ERROR * 100, "above error")
def log_exception():
try:
- raise ValueError('Bang!')
+ raise ValueError("Bang!")
except ValueError:
- logging.exception('Error occurred!')
+ logging.exception("Error occurred!")
def log_invalid_message():
@@ -63,17 +63,17 @@ def log_invalid_message():
def log_using_custom_logger():
- logging.getLogger('custom').info('custom logger')
+ logging.getLogger("custom").info("custom logger")
def log_using_non_propagating_logger():
- logging.getLogger('nonprop').info('nonprop logger')
+ logging.getLogger("nonprop").info("nonprop logger")
def log_messages_different_time():
- logging.info('First message')
+ logging.info("First message")
time.sleep(0.1)
- logging.info('Second message 0.1 sec later')
+ logging.info("Second message 0.1 sec later")
def log_with_format():
@@ -88,4 +88,10 @@ def log_with_format():
def log_something():
- logging.info('something')
+ logging.info("something")
+
+
+def log_non_strings():
+ logging.info(42)
+ logging.info(True)
+ logging.info(None)
diff --git a/atest/testdata/test_libraries/LibraryDecorator.py b/atest/testdata/test_libraries/LibraryDecorator.py
index 255099e0c10..f893259f43c 100644
--- a/atest/testdata/test_libraries/LibraryDecorator.py
+++ b/atest/testdata/test_libraries/LibraryDecorator.py
@@ -5,18 +5,26 @@
class LibraryDecorator:
def not_keyword(self):
- raise RuntimeError('Should not be executed!')
+ raise RuntimeError("Should not be executed!")
@keyword
def decorated_method_is_keyword(self):
- print('Decorated methods are keywords.')
+ print("Decorated methods are keywords.")
@staticmethod
@keyword
def decorated_static_method_is_keyword():
- print('Decorated static methods are keywords.')
+ print("Decorated static methods are keywords.")
@classmethod
@keyword
def decorated_class_method_is_keyword(cls):
- print('Decorated class methods are keywords.')
+ print("Decorated class methods are keywords.")
+
+
+@library(version="base")
+class DecoratedLibraryToBeExtended:
+
+ @keyword
+ def keyword_in_decorated_base_class(self):
+ pass
diff --git a/atest/testdata/test_libraries/LibraryDecoratorWithArgs.py b/atest/testdata/test_libraries/LibraryDecoratorWithArgs.py
index 7fcb0b24d7d..cc944f49fa5 100644
--- a/atest/testdata/test_libraries/LibraryDecoratorWithArgs.py
+++ b/atest/testdata/test_libraries/LibraryDecoratorWithArgs.py
@@ -1,20 +1,22 @@
from robot.api.deco import keyword, library
-@library(scope='SUITE', version='1.2.3', listener='self')
+@library(scope="SUITE", version="1.2.3", listener="self")
class LibraryDecoratorWithArgs:
start_suite_called = start_test_called = start_library_keyword_called = False
@keyword(name="Decorated method is keyword v.2")
def decorated_method_is_keyword(self):
- if not (self.start_suite_called and
- self.start_test_called and
- self.start_library_keyword_called):
- raise AssertionError('Listener methods are not called correctly!')
- print('Decorated methods are keywords.')
+ if not (
+ self.start_suite_called
+ and self.start_test_called
+ and self.start_library_keyword_called
+ ):
+ raise AssertionError("Listener methods are not called correctly!")
+ print("Decorated methods are keywords.")
def not_keyword_v2(self):
- raise RuntimeError('Should not be executed!')
+ raise RuntimeError("Should not be executed!")
def start_suite(self, data, result):
self.start_suite_called = True
diff --git a/atest/testdata/test_libraries/LibraryDecoratorWithAutoKeywords.py b/atest/testdata/test_libraries/LibraryDecoratorWithAutoKeywords.py
index 06af022d3d2..fdcf85a3d80 100644
--- a/atest/testdata/test_libraries/LibraryDecoratorWithAutoKeywords.py
+++ b/atest/testdata/test_libraries/LibraryDecoratorWithAutoKeywords.py
@@ -1,7 +1,7 @@
from robot.api.deco import keyword, library
-@library(scope='global', auto_keywords=True)
+@library(scope="global", auto_keywords=True)
class LibraryDecoratorWithAutoKeywords:
def undecorated_method_is_keyword(self):
diff --git a/atest/testdata/test_libraries/ModuleWitNotKeywordDecorator.py b/atest/testdata/test_libraries/ModuleWitNotKeywordDecorator.py
index 571473dd1be..58e4593d8d4 100644
--- a/atest/testdata/test_libraries/ModuleWitNotKeywordDecorator.py
+++ b/atest/testdata/test_libraries/ModuleWitNotKeywordDecorator.py
@@ -1,8 +1,7 @@
# None of these decorators should be exposed as keywords.
-from robot.api.deco import keyword, library, not_keyword
-
from os.path import abspath
+from robot.api.deco import keyword, not_keyword
not_keyword(abspath)
diff --git a/atest/testdata/test_libraries/ModuleWithAutoKeywordsOff.py b/atest/testdata/test_libraries/ModuleWithAutoKeywordsOff.py
index 041a6c2689c..d0d439ed03f 100644
--- a/atest/testdata/test_libraries/ModuleWithAutoKeywordsOff.py
+++ b/atest/testdata/test_libraries/ModuleWithAutoKeywordsOff.py
@@ -4,18 +4,18 @@
def public_method_is_not_keyword():
- raise RuntimeError('Should not be executed!')
+ raise RuntimeError("Should not be executed!")
@keyword(name="Decorated Method In Module Is Keyword")
def decorated_method():
- print('Decorated methods are keywords.')
+ print("Decorated methods are keywords.")
def _private_method_is_not_keyword():
- raise RuntimeError('Should not be executed!')
+ raise RuntimeError("Should not be executed!")
@keyword
def _private_decorated_method_in_module_is_keyword():
- print('Decorated private methods are keywords.')
+ print("Decorated private methods are keywords.")
diff --git a/atest/testdata/test_libraries/MyInvalidLibFile.py b/atest/testdata/test_libraries/MyInvalidLibFile.py
index d3876a27b02..23bec290277 100644
--- a/atest/testdata/test_libraries/MyInvalidLibFile.py
+++ b/atest/testdata/test_libraries/MyInvalidLibFile.py
@@ -1,2 +1 @@
raise ImportError("I'm not really a library!")
-
\ No newline at end of file
diff --git a/atest/testdata/test_libraries/MyLibDir/__init__.py b/atest/testdata/test_libraries/MyLibDir/__init__.py
index 22f7bf838e5..7ad26df6250 100644
--- a/atest/testdata/test_libraries/MyLibDir/__init__.py
+++ b/atest/testdata/test_libraries/MyLibDir/__init__.py
@@ -1,9 +1,10 @@
-from robot import utils
+from robot.utils import seq2str2
+
class MyLibDir:
-
+
def get_keyword_names(self):
- return ['Keyword In My Lib Dir']
-
+ return ["Keyword In My Lib Dir"]
+
def run_keyword(self, name, args):
- return "Executed keyword '%s' with args %s" % (name, utils.seq2str2(args))
+ return f"Executed keyword '{name}' with args {seq2str2(args)}"
diff --git a/atest/testdata/test_libraries/MyLibFile.py b/atest/testdata/test_libraries/MyLibFile.py
index d6f489cae4d..b923492fa5a 100644
--- a/atest/testdata/test_libraries/MyLibFile.py
+++ b/atest/testdata/test_libraries/MyLibFile.py
@@ -1,7 +1,9 @@
def keyword_in_my_lib_file():
- print('Here we go!!')
+ print("Here we go!!")
+
def embedded(arg):
print(arg)
-embedded.robot_name = 'Keyword with embedded ${arg} in MyLibFile'
+
+embedded.robot_name = "Keyword with embedded ${arg} in MyLibFile"
diff --git a/atest/testdata/test_libraries/NamedArgsImportLibrary.py b/atest/testdata/test_libraries/NamedArgsImportLibrary.py
index fd1276e8707..5cd62ad04d1 100644
--- a/atest/testdata/test_libraries/NamedArgsImportLibrary.py
+++ b/atest/testdata/test_libraries/NamedArgsImportLibrary.py
@@ -5,7 +5,10 @@ def __init__(self, arg1=None, arg2=None, **kws):
self.arg2 = arg2
self.kws = kws
- def check_init_arguments(self, exp_arg1, exp_arg2, **kws):
- if self.arg1 != exp_arg1 or self.arg2 != exp_arg2 or kws != self.kws:
- raise AssertionError('Wrong initialization values. Got (%s, %s, %r), expected (%s, %s, %r)'
- % (self.arg1, self.arg2, self.kws, exp_arg1, exp_arg2, kws))
+ def check_init_arguments(self, arg1, arg2, **kws):
+ if self.arg1 != arg1 or self.arg2 != arg2 or kws != self.kws:
+ raise AssertionError(
+ f"Wrong initialization values. "
+ f"Got ({self.arg1!r}, {self.arg2!r}, {self.kws!r}), "
+ f"expected ({arg1!r}, {arg2!r}, {kws!r})"
+ )
diff --git a/atest/testdata/test_libraries/PartialFunction.py b/atest/testdata/test_libraries/PartialFunction.py
index 5dd0e50058a..101b9ae7965 100644
--- a/atest/testdata/test_libraries/PartialFunction.py
+++ b/atest/testdata/test_libraries/PartialFunction.py
@@ -7,4 +7,4 @@ def function(value, expected, lower=False):
assert value == expected
-partial_function = partial(function, expected='value')
+partial_function = partial(function, expected="value")
diff --git a/atest/testdata/test_libraries/PartialMethod.py b/atest/testdata/test_libraries/PartialMethod.py
index 988c7f1960c..4502fa78c21 100644
--- a/atest/testdata/test_libraries/PartialMethod.py
+++ b/atest/testdata/test_libraries/PartialMethod.py
@@ -8,4 +8,4 @@ def method(self, value, expected, lower: bool = False):
value = value.lower()
assert value == expected
- partial_method = partialmethod(method, expected='value')
+ partial_method = partialmethod(method, expected="value")
diff --git a/atest/testdata/test_libraries/PrintLib.py b/atest/testdata/test_libraries/PrintLib.py
index 3b78a533570..261b58b28bb 100644
--- a/atest/testdata/test_libraries/PrintLib.py
+++ b/atest/testdata/test_libraries/PrintLib.py
@@ -6,20 +6,20 @@ def print_one_html_line():
def print_many_html_lines():
- print('*HTML*
\n
0,0
0,1
')
- print('
1,0
1,1
\n
')
- print('*HTML*This is html ')
- print('*INFO*This is not html ')
+ print("*HTML*
\n
0,0
0,1
")
+ print("
1,0
1,1
\n
")
+ print("*HTML*This is html ")
+ print("*INFO*This is not html ")
def print_html_to_stderr():
- print('*HTML* Hello, stderr!!', file=sys.stderr)
+ print("*HTML* Hello, stderr!!", file=sys.stderr)
def print_console():
- print('*CONSOLE* Hello info and console!')
+ print("*CONSOLE* Hello info and console!")
def print_with_all_levels():
- for level in 'TRACE DEBUG INFO CONSOLE HTML WARN ERROR'.split():
- print('*%s* %s message' % (level, level.title()))
+ for level in "TRACE DEBUG INFO CONSOLE HTML WARN ERROR".split():
+ print(f"*{level}* {level.title()} message")
diff --git a/atest/testdata/test_libraries/PythonLibUsingTimestamps.py b/atest/testdata/test_libraries/PythonLibUsingTimestamps.py
index 3aac1be0714..e0a5930d772 100644
--- a/atest/testdata/test_libraries/PythonLibUsingTimestamps.py
+++ b/atest/testdata/test_libraries/PythonLibUsingTimestamps.py
@@ -6,14 +6,18 @@ def timezone_correction():
tz = 7200 + time.timezone
return (tz + dst) * 1000
+
def timestamp_as_integer():
- t = 1308419034931 + timezone_correction()
- print('*INFO:%d* Known timestamp' % t)
- print('*HTML:%d* Current' % int(time.time() * 1000))
+ known = 1308419034931 + timezone_correction()
+ current = int(time.time() * 1000)
+ print(f"*INFO:{known}* Known timestamp")
+ print(f"*HTML:{current}* Current")
time.sleep(0.1)
+
def timestamp_as_float():
- t = 1308419034930.502342313 + timezone_correction()
- print('*INFO:%f* Known timestamp' % t)
- print('*HTML:%f* Current' % float(time.time() * 1000))
+ known = 1308419034930.502342313 + timezone_correction()
+ current = float(time.time() * 1000)
+ print(f"*INFO:{known}* Known timestamp")
+ print(f"*HTML:{current}* Current")
time.sleep(0.1)
diff --git a/atest/testdata/test_libraries/ThreadLoggingLib.py b/atest/testdata/test_libraries/ThreadLoggingLib.py
index 58c1799353c..062779c685a 100644
--- a/atest/testdata/test_libraries/ThreadLoggingLib.py
+++ b/atest/testdata/test_libraries/ThreadLoggingLib.py
@@ -1,22 +1,24 @@
-import threading
import logging
+import threading
import time
from robot.api import logger
-
def log_using_robot_api_in_thread():
threading.Timer(0.1, log_using_robot_api).start()
+
def log_using_robot_api():
for i in range(100):
logger.info(str(i))
time.sleep(0.01)
+
def log_using_logging_module_in_thread():
threading.Timer(0.1, log_using_logging_module).start()
+
def log_using_logging_module():
for i in range(100):
logging.info(str(i))
diff --git a/atest/testdata/test_libraries/as_listener/LogLevels.py b/atest/testdata/test_libraries/as_listener/LogLevels.py
index a1b71e35abc..1bbe55d7d6a 100644
--- a/atest/testdata/test_libraries/as_listener/LogLevels.py
+++ b/atest/testdata/test_libraries/as_listener/LogLevels.py
@@ -9,7 +9,7 @@ def __init__(self):
self.messages = []
def _log_message(self, msg):
- self.messages.append('%s: %s' % (msg['level'], msg['message']))
+ self.messages.append(f"{msg['level']}: {msg['message']}")
def logged_messages_should_be(self, *expected):
- BuiltIn().should_be_equal('\n'.join(self.messages), '\n'.join(expected))
+ BuiltIn().should_be_equal("\n".join(self.messages), "\n".join(expected))
diff --git a/atest/testdata/test_libraries/as_listener/empty_listenerlibrary.py b/atest/testdata/test_libraries/as_listener/empty_listenerlibrary.py
index 48f1ea4a6e2..1cf69883ab0 100644
--- a/atest/testdata/test_libraries/as_listener/empty_listenerlibrary.py
+++ b/atest/testdata/test_libraries/as_listener/empty_listenerlibrary.py
@@ -1,10 +1,10 @@
-from robot.api.deco import library
-
import sys
+from robot.api.deco import library
+
class listener:
- ROBOT_LISTENER_API_VERSION = '2'
+ ROBOT_LISTENER_API_VERSION = "2"
def start_test(self, name, attrs):
self._stderr("START TEST")
@@ -13,15 +13,15 @@ def end_test(self, name, attrs):
self._stderr("END TEST")
def log_message(self, msg):
- self._stderr("MESSAGE %s" % msg['message'])
+ self._stderr(f"MESSAGE {msg['message']}")
def close(self):
self._stderr("CLOSE")
def _stderr(self, msg):
- sys.__stderr__.write("%s\n" % msg)
+ sys.__stderr__.write(f"{msg}\n")
-@library(scope='TEST CASE', listener=listener())
+@library(scope="TEST", listener=listener())
class empty_listenerlibrary:
pass
diff --git a/atest/testdata/test_libraries/as_listener/global_vars_listenerlibrary.py b/atest/testdata/test_libraries/as_listener/global_vars_listenerlibrary.py
index a560dea7af7..f0b94ecd3c9 100644
--- a/atest/testdata/test_libraries/as_listener/global_vars_listenerlibrary.py
+++ b/atest/testdata/test_libraries/as_listener/global_vars_listenerlibrary.py
@@ -3,18 +3,21 @@
from robot.libraries.BuiltIn import BuiltIn
-class global_vars_listenerlibrary():
+class global_vars_listenerlibrary:
ROBOT_LISTENER_API_VERSION = 2
- global_vars = ["${SUITE_NAME}",
- "${SUITE_DOCUMENTATION}",
- "${PREV_TEST_NAME}",
- "${PREV_TEST_STATUS}",
- "${LOG_LEVEL}"]
+ global_vars = [
+ "${SUITE_NAME}",
+ "${SUITE_DOCUMENTATION}",
+ "${PREV_TEST_NAME}",
+ "${PREV_TEST_STATUS}",
+ "${LOG_LEVEL}",
+ ]
def __init__(self):
self.ROBOT_LIBRARY_LISTENER = self
def _close(self):
+ get_variable_value = BuiltIn().get_variable_value
for var in self.global_vars:
- sys.__stderr__.write('%s: %s\n' % (var, BuiltIn().get_variable_value(var)))
+ sys.__stderr__.write(f"{var}: {get_variable_value(var)}\n")
diff --git a/atest/testdata/test_libraries/as_listener/global_vars_listenerlibrary_global_scope.py b/atest/testdata/test_libraries/as_listener/global_vars_listenerlibrary_global_scope.py
index 7357f3f3a16..9f020316988 100644
--- a/atest/testdata/test_libraries/as_listener/global_vars_listenerlibrary_global_scope.py
+++ b/atest/testdata/test_libraries/as_listener/global_vars_listenerlibrary_global_scope.py
@@ -2,4 +2,4 @@
class global_vars_listenerlibrary_global_scope(global_vars_listenerlibrary):
- ROBOT_LIBRARY_SCOPE = 'GLOBAL'
+ ROBOT_LIBRARY_SCOPE = "GLOBAL"
diff --git a/atest/testdata/test_libraries/as_listener/global_vars_listenerlibrary_ts_scope.py b/atest/testdata/test_libraries/as_listener/global_vars_listenerlibrary_ts_scope.py
index ae2d5242555..c150a29d265 100644
--- a/atest/testdata/test_libraries/as_listener/global_vars_listenerlibrary_ts_scope.py
+++ b/atest/testdata/test_libraries/as_listener/global_vars_listenerlibrary_ts_scope.py
@@ -2,4 +2,4 @@
class global_vars_listenerlibrary_ts_scope(global_vars_listenerlibrary):
- ROBOT_LIBRARY_SCOPE = 'TEST SUITE'
+ ROBOT_LIBRARY_SCOPE = "TEST SUITE"
diff --git a/atest/testdata/test_libraries/as_listener/listenerlibrary.py b/atest/testdata/test_libraries/as_listener/listenerlibrary.py
index 5a4db19a67d..77a64a2250d 100644
--- a/atest/testdata/test_libraries/as_listener/listenerlibrary.py
+++ b/atest/testdata/test_libraries/as_listener/listenerlibrary.py
@@ -8,48 +8,52 @@ class listenerlibrary:
def __init__(self):
self.ROBOT_LIBRARY_LISTENER = self
self.events = []
- self.level = 'suite'
+ self.level = "suite"
def get_events(self):
return self.events[:]
def _start_suite(self, name, attrs):
- self.events.append('Start suite: %s' % name)
+ self.events.append(f"Start suite: {name}")
def endSuite(self, name, attrs):
- self.events.append('End suite: %s' % name)
+ self.events.append(f"End suite: {name}")
def _start_test(self, name, attrs):
- self.events.append('Start test: %s' % name)
- self.level = 'test'
+ self.events.append(f"Start test: {name}")
+ self.level = "test"
def end_test(self, name, attrs):
- self.events.append('End test: %s' % name)
+ self.events.append(f"End test: {name}")
def _startKeyword(self, name, attrs):
- self.events.append('Start kw: %s' % name)
+ self.events.append(f"Start kw: {name}")
def _end_keyword(self, name, attrs):
- self.events.append('End kw: %s' % name)
+ self.events.append(f"End kw: {name}")
def _close(self):
- if self.ROBOT_LIBRARY_SCOPE == 'TEST CASE':
- level = ' (%s)' % self.level
+ if self.ROBOT_LIBRARY_SCOPE == "TEST CASE":
+ level = f" ({self.level})"
else:
- level = ''
- sys.__stderr__.write("CLOSING %s%s\n" % (self.ROBOT_LIBRARY_SCOPE, level))
+ level = ""
+ sys.__stderr__.write(f"CLOSING {self.ROBOT_LIBRARY_SCOPE}{level}\n")
def events_should_be(self, *expected):
- self._assert(self.events == list(expected),
- 'Expected events:\n%s\n\nActual events:\n%s'
- % (self._format(expected), self._format(self.events)))
+ self._assert(
+ self.events == list(expected),
+ f"Expected events:\n{self._format(expected)}\n\n"
+ f"Actual events:\n{self._format(self.events)}",
+ )
def events_should_be_empty(self):
- self._assert(not self.events,
- 'Expected no events, got:\n%s' % self._format(self.events))
+ self._assert(
+ not self.events,
+ f"Expected no events, got:\n{self._format(self.events)}",
+ )
def _assert(self, condition, message):
assert condition, message
def _format(self, events):
- return '\n'.join(events)
+ return "\n".join(events)
diff --git a/atest/testdata/test_libraries/as_listener/listenerlibrary3.py b/atest/testdata/test_libraries/as_listener/listenerlibrary3.py
index adb5f44a35f..d326b0c96d0 100644
--- a/atest/testdata/test_libraries/as_listener/listenerlibrary3.py
+++ b/atest/testdata/test_libraries/as_listener/listenerlibrary3.py
@@ -2,57 +2,57 @@
class listenerlibrary3:
- ROBOT_LIBRARY_LISTENER = 'SELF'
+ ROBOT_LIBRARY_LISTENER = "SELF"
def __init__(self):
self.listeners = []
def start_suite(self, data, result):
- result.doc = (result.doc + ' [start suite]').strip()
- result.metadata['suite'] = '[start]'
- result.metadata['tests'] = ''
+ result.doc = (result.doc + " [start suite]").strip()
+ result.metadata["suite"] = "[start]"
+ result.metadata["tests"] = ""
assert len(data.tests) == 2
assert len(result.tests) == 0
- data.tests.create(name='New')
+ data.tests.create(name="New")
assert not self.listeners or self.listeners[-1] is not self
self.listeners.append(self)
def end_suite(self, data, result):
assert len(data.tests) == 3
assert len(result.tests) == 3
- assert result.doc.endswith('[start suite]')
- assert result.metadata['suite'] == '[start]'
- result.name += ' [end suite]'
- result.doc += ' [end suite]'
- result.metadata['suite'] += ' [end]'
+ assert result.doc.endswith("[start suite]")
+ assert result.metadata["suite"] == "[start]"
+ result.name += " [end suite]"
+ result.doc += " [end suite]"
+ result.metadata["suite"] += " [end]"
assert self.listeners.pop() is self
def start_test(self, data, result):
- result.doc = (result.doc + ' [start test]').strip()
- result.tags.add('[start]')
- result.message = 'Message: [start]'
- result.parent.metadata['tests'] += 'x'
- data.body.create_keyword('No Operation')
+ result.doc = (result.doc + " [start test]").strip()
+ result.tags.add("[start]")
+ result.message = "Message: [start]"
+ result.parent.metadata["tests"] += "x"
+ data.body.create_keyword("No Operation")
assert not self.listeners or self.listeners[-1] is not self
self.listeners.append(self)
def end_test(self, data, result):
- result.doc += ' [end test]'
- result.tags.add('[end]')
+ result.doc += " [end test]"
+ result.tags.add("[end]")
result.passed = not result.passed
- result.message += ' [end]'
+ result.message += " [end]"
assert self.listeners.pop() is self
def log_message(self, msg):
- msg.message += ' [log_message]'
- msg.timestamp = '2015-12-16 15:51:20.141'
+ msg.message += " [log_message]"
+ msg.timestamp = "2015-12-16 15:51:20.141"
def foo(self):
print("*WARN* Foo")
def message(self, msg):
- msg.message += ' [message]'
- msg.timestamp = '2015-12-16 15:51:20.141'
+ msg.message += " [message]"
+ msg.timestamp = "2015-12-16 15:51:20.141"
def close(self):
- sys.__stderr__.write('CLOSING Listener library 3\n')
+ sys.__stderr__.write("CLOSING Listener library 3\n")
diff --git a/atest/testdata/test_libraries/as_listener/multiple_listenerlibrary.py b/atest/testdata/test_libraries/as_listener/multiple_listenerlibrary.py
index 4f9c19ef9a7..d28351ee20b 100644
--- a/atest/testdata/test_libraries/as_listener/multiple_listenerlibrary.py
+++ b/atest/testdata/test_libraries/as_listener/multiple_listenerlibrary.py
@@ -9,10 +9,13 @@ def __init__(self, fail=False):
listenerlibrary(),
]
if fail:
+
class BadVersionListener:
ROBOT_LISTENER_API_VERSION = 666
+
def events_should_be_empty(self):
pass
+
self.instances.append(BadVersionListener())
self.ROBOT_LIBRARY_LISTENER = self.instances
diff --git a/atest/testdata/test_libraries/avoid_properties_when_creating_libraries.robot b/atest/testdata/test_libraries/avoid_properties_when_creating_libraries.robot
index fe8f94ef578..a3adcfb95c0 100644
--- a/atest/testdata/test_libraries/avoid_properties_when_creating_libraries.robot
+++ b/atest/testdata/test_libraries/avoid_properties_when_creating_libraries.robot
@@ -8,7 +8,7 @@ Property
normal_property
Classmethod property
- classmethod_property classmethod=True
+ classmethod_property
Cached property
cached_property
@@ -17,19 +17,19 @@ Non-data descriptor
non_data_descriptor 2
Classmethod non-data descriptor
- classmethod_non_data_descriptor 2 classmethod=True
+ classmethod_non_data_descriptor
Data descriptor
data_descriptor
Classmethod data descriptor
- classmethod_data_descriptor classmethod=True
+ classmethod_data_descriptor
*** Keywords ***
Attribute value should be
- [Arguments] ${attr} ${expected}=1 ${classmethod}=False
- ${lib} = Get Library Instance AvoidProperties
- IF sys.version_info >= (3, 9) or not ${classmethod}
+ [Arguments] ${attr} ${expected}=1
+ IF 'classmethod' not in $attr
+ ${lib} = Get Library Instance AvoidProperties
Should Be Equal As Integers ${lib.${attr}} ${expected}
END
TRY
diff --git a/atest/testdata/test_libraries/custom_dir.robot b/atest/testdata/test_libraries/custom_dir.robot
new file mode 100644
index 00000000000..3ff66195f23
--- /dev/null
+++ b/atest/testdata/test_libraries/custom_dir.robot
@@ -0,0 +1,9 @@
+*** Settings ***
+Library CustomDir.py
+
+*** Test Cases ***
+Normal keyword
+ Normal arg
+
+Keyword implemented via getattr
+ Via getattr arg
diff --git a/atest/testdata/test_libraries/dir_for_libs/MyLibFile2.py b/atest/testdata/test_libraries/dir_for_libs/MyLibFile2.py
index d14ad20291c..27a0ce3385b 100644
--- a/atest/testdata/test_libraries/dir_for_libs/MyLibFile2.py
+++ b/atest/testdata/test_libraries/dir_for_libs/MyLibFile2.py
@@ -1,4 +1,4 @@
class MyLibFile2:
def keyword_in_my_lib_file_2(self, arg):
- return 'Hello %s!' % arg
+ return f"Hello {arg}!"
diff --git a/atest/testdata/test_libraries/dir_for_libs/lib1/Lib.py b/atest/testdata/test_libraries/dir_for_libs/lib1/Lib.py
index b0b3b304e39..1edcd100447 100644
--- a/atest/testdata/test_libraries/dir_for_libs/lib1/Lib.py
+++ b/atest/testdata/test_libraries/dir_for_libs/lib1/Lib.py
@@ -1,7 +1,7 @@
class Lib:
def hello(self):
- print('Hello from lib1')
+ print("Hello from lib1")
def kw_from_lib1(self):
pass
diff --git a/atest/testdata/test_libraries/dir_for_libs/lib2/Lib.py b/atest/testdata/test_libraries/dir_for_libs/lib2/Lib.py
index c72e1d0e16a..d6e42cf1922 100644
--- a/atest/testdata/test_libraries/dir_for_libs/lib2/Lib.py
+++ b/atest/testdata/test_libraries/dir_for_libs/lib2/Lib.py
@@ -1,5 +1,6 @@
def hello():
- print('Hello from lib2')
+ print("Hello from lib2")
+
def kw_from_lib2():
pass
diff --git a/atest/testdata/test_libraries/dynamic_libraries/AsyncDynamicLibrary.py b/atest/testdata/test_libraries/dynamic_libraries/AsyncDynamicLibrary.py
index 0c9d24c5e44..b751c16dcf6 100644
--- a/atest/testdata/test_libraries/dynamic_libraries/AsyncDynamicLibrary.py
+++ b/atest/testdata/test_libraries/dynamic_libraries/AsyncDynamicLibrary.py
@@ -7,8 +7,11 @@ async def get_keyword_names(self):
await asyncio.sleep(0.1)
return ["async_keyword"]
- async def run_keyword(self, name, *args, **kwargs):
- print("Running keyword '%s' with positional arguments %s and named arguments %s." % (name, args, kwargs))
+ async def run_keyword(self, name, *args, **named):
+ print(
+ f"Running keyword '{name}' with positional arguments {args} "
+ f"and named arguments {named}."
+ )
await asyncio.sleep(0.1)
if name == "async_keyword":
return await self.async_keyword()
diff --git a/atest/testdata/test_libraries/dynamic_libraries/DynamicLibraryWithKwargsSupportWithoutArgspec.py b/atest/testdata/test_libraries/dynamic_libraries/DynamicLibraryWithKwargsSupportWithoutArgspec.py
index 81e3264e4f7..387f79b8e34 100644
--- a/atest/testdata/test_libraries/dynamic_libraries/DynamicLibraryWithKwargsSupportWithoutArgspec.py
+++ b/atest/testdata/test_libraries/dynamic_libraries/DynamicLibraryWithKwargsSupportWithoutArgspec.py
@@ -7,4 +7,4 @@ def run_keyword(self, name, args, kwargs):
return getattr(self, name)(*args, **kwargs)
def do_something_with_kwargs(self, a, b=2, c=3, **kwargs):
- print(a, b, c, ' '.join('%s:%s' % (k, v) for k, v in kwargs.items()))
+ print(a, b, c, " ".join(f"{k}:{kwargs[k]}" for k in kwargs))
diff --git a/atest/testdata/test_libraries/dynamic_libraries/DynamicLibraryWithoutArgspec.py b/atest/testdata/test_libraries/dynamic_libraries/DynamicLibraryWithoutArgspec.py
index 51105e70aaf..48d47240e8d 100644
--- a/atest/testdata/test_libraries/dynamic_libraries/DynamicLibraryWithoutArgspec.py
+++ b/atest/testdata/test_libraries/dynamic_libraries/DynamicLibraryWithoutArgspec.py
@@ -1,7 +1,7 @@
class DynamicLibraryWithoutArgspec:
def get_keyword_names(self):
- return [name for name in dir(self) if name.startswith('do_')]
+ return [name for name in dir(self) if name.startswith("do_")]
def run_keyword(self, name, args):
return getattr(self, name)(*args)
@@ -10,7 +10,7 @@ def do_something(self, x):
print(x)
def do_something_else(self, x, y=0):
- print('x: %s, y: %s' % (x, y))
+ print(f"x: {x}, y: {y}")
def do_something_third(self, a, b=2, c=3):
print(a, b, c)
diff --git a/atest/testdata/test_libraries/dynamic_libraries/EmbeddedArgs.py b/atest/testdata/test_libraries/dynamic_libraries/EmbeddedArgs.py
index 1ab656b1f5f..d86f034c533 100755
--- a/atest/testdata/test_libraries/dynamic_libraries/EmbeddedArgs.py
+++ b/atest/testdata/test_libraries/dynamic_libraries/EmbeddedArgs.py
@@ -1,8 +1,8 @@
class EmbeddedArgs:
def get_keyword_names(self):
- return ['Add ${count} Copies Of ${item} To Cart']
+ return ["Add ${count} Copies Of ${item} To Cart"]
def run_keyword(self, name, args):
- assert name == 'Add ${count} Copies Of ${item} To Cart'
+ assert name == "Add ${count} Copies Of ${item} To Cart"
return args
diff --git a/atest/testdata/test_libraries/dynamic_libraries/InvalidArgSpecs.py b/atest/testdata/test_libraries/dynamic_libraries/InvalidArgSpecs.py
index 2170d79d5e2..78e989250cb 100644
--- a/atest/testdata/test_libraries/dynamic_libraries/InvalidArgSpecs.py
+++ b/atest/testdata/test_libraries/dynamic_libraries/InvalidArgSpecs.py
@@ -1,16 +1,18 @@
-KEYWORDS = [('other than strings', [1, 2]),
- ('named args before positional', ['a=1', 'b']),
- ('multiple varargs', ['*first', '*second']),
- ('kwargs before positional args', ['**kwargs', 'a']),
- ('kwargs before named args', ['**kwargs', 'a=1']),
- ('kwargs before varargs', ['**kwargs', '*varargs']),
- ('empty tuple', ['arg', ()]),
- ('too long tuple', [('too', 'long', 'tuple')]),
- ('too long tuple with *varargs', [('*too', 'long')]),
- ('too long tuple with **kwargs', [('**too', 'long')]),
- ('tuple with non-string first value', [(None,)]),
- ('valid argspec', ['a']),
- ('valid argspec with tuple', [['a'], ('b', None)])]
+KEYWORDS = [
+ ("other than strings", [1, 2]),
+ ("named args before positional", ["a=1", "b"]),
+ ("multiple varargs", ["*first", "*second"]),
+ ("kwargs before positional args", ["**kwargs", "a"]),
+ ("kwargs before named args", ["**kwargs", "a=1"]),
+ ("kwargs before varargs", ["**kwargs", "*varargs"]),
+ ("empty tuple", ["arg", ()]),
+ ("too long tuple", [("too", "long", "tuple")]),
+ ("too long tuple with *varargs", [("*too", "long")]),
+ ("too long tuple with **kwargs", [("**too", "long")]),
+ ("tuple with non-string first value", [(None,)]),
+ ("valid argspec", ["a"]),
+ ("valid argspec with tuple", [["a"], ("b", None)]),
+]
class InvalidArgSpecs:
@@ -19,7 +21,7 @@ def get_keyword_names(self):
return [name for name, _ in KEYWORDS]
def run_keyword(self, name, args, kwargs):
- return ' '.join(args + tuple(kwargs)).upper()
+ return " ".join(args + tuple(kwargs)).upper()
def get_keyword_arguments(self, name):
return dict(KEYWORDS)[name]
diff --git a/atest/testdata/test_libraries/dynamic_libraries/NonAsciiKeywordNames.py b/atest/testdata/test_libraries/dynamic_libraries/NonAsciiKeywordNames.py
index 8a0133a2f59..be9d58ec261 100644
--- a/atest/testdata/test_libraries/dynamic_libraries/NonAsciiKeywordNames.py
+++ b/atest/testdata/test_libraries/dynamic_libraries/NonAsciiKeywordNames.py
@@ -1,11 +1,13 @@
class NonAsciiKeywordNames:
def __init__(self, include_latin1=False):
- self.names = ['Unicode nön-äscïï',
- '\u2603', # snowman
- 'UTF-8 nön-äscïï'.encode('UTF-8')]
+ self.names = [
+ "Unicode nön-äscïï",
+ "\u2603", # snowman
+ "UTF-8 nön-äscïï".encode("UTF-8"),
+ ]
if include_latin1:
- self.names.append('Latin1 nön-äscïï'.encode('latin1'))
+ self.names.append("Latin1 nön-äscïï".encode("latin1"))
def get_keyword_names(self):
return self.names
diff --git a/atest/testdata/test_libraries/error_msg_and_details.robot b/atest/testdata/test_libraries/error_msg_and_details.robot
index 7c260d5be3e..acde316af5e 100644
--- a/atest/testdata/test_libraries/error_msg_and_details.robot
+++ b/atest/testdata/test_libraries/error_msg_and_details.robot
@@ -3,14 +3,22 @@ Library ExampleLibrary
Library nön_äscii_dïr/valid.py
*** Test Cases ***
-Generic Failure
+Generic failure
[Documentation] FAIL foo != bar
Exception AssertionError foo != bar
-Exception Name Suppressed in Error Message
+Exception name suppressed explicitly
[Documentation] FAIL No Exception Name
Fail with suppressed exception name No Exception Name
+Even suppressed name is included if message is empty
+ [Documentation] FAIL ExceptionWithSuppressedName
+ Fail with suppressed exception name ${EMPTY}
+
+Exception with empty message and name is handled properly
+ [Documentation] FAIL
+ Exception with empty message and name
+
Non Generic Failure
[Documentation] FAIL FloatingPointError: Too Large A Number !!
Exception FloatingPointError Too Large A Number !!
diff --git a/atest/testdata/test_libraries/extend_decorated_library.py b/atest/testdata/test_libraries/extend_decorated_library.py
new file mode 100644
index 00000000000..231817d5018
--- /dev/null
+++ b/atest/testdata/test_libraries/extend_decorated_library.py
@@ -0,0 +1,13 @@
+# Imported decorated classes are not considered libraries automatically.
+from LibraryDecorator import DecoratedLibraryToBeExtended
+from multiple_library_decorators import Class1, Class2, Class3 # noqa: F401
+
+from robot.api.deco import keyword, library
+
+
+@library(version="extended")
+class ExtendedLibrary(DecoratedLibraryToBeExtended):
+
+ @keyword
+ def keyword_in_decorated_extending_class(self):
+ pass
diff --git a/atest/testdata/test_libraries/library_decorator.robot b/atest/testdata/test_libraries/library_decorator.robot
index 2498c65c454..463ff7e6801 100644
--- a/atest/testdata/test_libraries/library_decorator.robot
+++ b/atest/testdata/test_libraries/library_decorator.robot
@@ -2,6 +2,11 @@
Library LibraryDecorator.py
Library LibraryDecoratorWithArgs.py
Library LibraryDecoratorWithAutoKeywords.py
+Library library_decorator_when_class_and_module_names_do_not_match.py
+Library extend_decorated_library.py
+Library multiple_library_decorators.Class2
+Library multiple_library_decorators.Class3
+Library multiple_library_decorators.py
*** Test Cases ***
Library decorator disables automatic keyword discovery
@@ -21,3 +26,19 @@ Library decorator with arguments disables automatic keyword discovery by default
Library decorator can enable automatic keyword discovery
Undecorated method is keyword
Decorated method is keyword as well
+
+When importing a module and there is one decorated class, the class is used as a library
+ Class name does not match module name
+
+When importing a module and there are multiple decorated classes, the module is used as a library
+ Module keyword
+
+When importing class explicitly, module can have multiple decorated classes
+ [Documentation] FAIL STARTS: No keyword with name 'Class 1 keyword' found. Did you mean:
+ Class 2 keyword
+ Class 3 keyword
+ Class 1 keyword
+
+Imported decorated classes are not considered to be libraries automatically
+ Keyword in decorated base class
+ Keyword in decorated extending class
diff --git a/atest/testdata/test_libraries/library_decorator_when_class_and_module_names_do_not_match.py b/atest/testdata/test_libraries/library_decorator_when_class_and_module_names_do_not_match.py
new file mode 100644
index 00000000000..ccfb613ff99
--- /dev/null
+++ b/atest/testdata/test_libraries/library_decorator_when_class_and_module_names_do_not_match.py
@@ -0,0 +1,8 @@
+from robot.api.deco import library
+
+
+@library(auto_keywords=True)
+class Library:
+
+ def class_name_does_not_match_module_name(self):
+ pass
diff --git a/atest/testdata/test_libraries/logging_with_logging.robot b/atest/testdata/test_libraries/logging_with_logging.robot
index b095762e2cd..455095b032c 100644
--- a/atest/testdata/test_libraries/logging_with_logging.robot
+++ b/atest/testdata/test_libraries/logging_with_logging.robot
@@ -40,3 +40,6 @@ Logging when timeout is in use
Log with format
Log with format
+
+Log non-strings
+ Log non strings
diff --git a/atest/testdata/test_libraries/module_lib_with_all.py b/atest/testdata/test_libraries/module_lib_with_all.py
index a42014ef40f..ca3ec86311b 100644
--- a/atest/testdata/test_libraries/module_lib_with_all.py
+++ b/atest/testdata/test_libraries/module_lib_with_all.py
@@ -1,15 +1,25 @@
-from os.path import join, abspath
+from os.path import abspath, join
+
+__all__ = [
+ "join_with_execdir",
+ "abspath",
+ "attr_is_not_kw",
+ "_not_kw_even_if_listed_in_all",
+ "extra stuff", # noqa: F822
+ None,
+]
-__all__ = ['join_with_execdir', 'abspath', 'attr_is_not_kw',
- '_not_kw_even_if_listed_in_all', 'extra stuff', None]
def join_with_execdir(arg):
- return join(abspath('.'), arg)
+ return join(abspath("."), arg)
+
def not_in_all():
pass
-attr_is_not_kw = 'Listed in __all__ but not a fuction'
+
+attr_is_not_kw = "Listed in __all__ but not a fuction"
+
def _not_kw_even_if_listed_in_all():
- print('Listed in __all__ but starts with an underscore')
+ print("Listed in __all__ but starts with an underscore")
diff --git a/atest/testdata/test_libraries/multiple_library_decorators.py b/atest/testdata/test_libraries/multiple_library_decorators.py
new file mode 100644
index 00000000000..b8528a378b3
--- /dev/null
+++ b/atest/testdata/test_libraries/multiple_library_decorators.py
@@ -0,0 +1,26 @@
+from robot.api.deco import keyword, library
+
+
+@library
+class Class1:
+ @keyword
+ def class1_keyword(self):
+ pass
+
+
+@library(scope="SUITE")
+class Class2:
+ @keyword
+ def class2_keyword(self):
+ pass
+
+
+@library
+class Class3:
+ @keyword
+ def class3_keyword(self):
+ pass
+
+
+def module_keyword():
+ pass
diff --git "a/atest/testdata/test_libraries/n\303\266n_\303\244scii_d\303\257r/invalid.py" "b/atest/testdata/test_libraries/n\303\266n_\303\244scii_d\303\257r/invalid.py"
index 893227ffbe8..8ccfa41c392 100644
--- "a/atest/testdata/test_libraries/n\303\266n_\303\244scii_d\303\257r/invalid.py"
+++ "b/atest/testdata/test_libraries/n\303\266n_\303\244scii_d\303\257r/invalid.py"
@@ -1 +1 @@
-raise RuntimeError('Ööööps!')
+raise RuntimeError("Ööööps!")
diff --git a/atest/testdata/test_libraries/run_logging_tests_on_thread.py b/atest/testdata/test_libraries/run_logging_tests_on_thread.py
new file mode 100644
index 00000000000..02d6cee0f0e
--- /dev/null
+++ b/atest/testdata/test_libraries/run_logging_tests_on_thread.py
@@ -0,0 +1,29 @@
+import sys
+from pathlib import Path
+from threading import Thread
+
+CURDIR = Path(__file__).parent.absolute()
+sys.path.insert(0, str(CURDIR / "../../../src"))
+sys.path.insert(1, str(CURDIR / "../../testresources/testlibs"))
+
+
+from robot import run # noqa: E402
+
+
+def run_logging_tests(output):
+ run(
+ CURDIR / "logging_api.robot",
+ CURDIR / "logging_with_logging.robot",
+ CURDIR / "print_logging.robot",
+ name="Logging tests",
+ dotted=True,
+ output=output,
+ report=None,
+ log=None,
+ )
+
+
+output = (*sys.argv, "output.xml")[1]
+t = Thread(target=lambda: run_logging_tests(output))
+t.start()
+t.join()
diff --git a/atest/testdata/variables/DynamicPythonClass.py b/atest/testdata/variables/DynamicPythonClass.py
index d19a7b763b6..62ad53845bf 100644
--- a/atest/testdata/variables/DynamicPythonClass.py
+++ b/atest/testdata/variables/DynamicPythonClass.py
@@ -1,5 +1,7 @@
class DynamicPythonClass:
def get_variables(self, *args):
- return {'dynamic_python_string': ' '.join(args),
- 'LIST__dynamic_python_list': args}
+ return {
+ "dynamic_python_string": " ".join(args),
+ "LIST__dynamic_python_list": args,
+ }
diff --git a/atest/testdata/variables/PythonClass.py b/atest/testdata/variables/PythonClass.py
index 9db271be36f..4d3bb401a72 100644
--- a/atest/testdata/variables/PythonClass.py
+++ b/atest/testdata/variables/PythonClass.py
@@ -1,7 +1,7 @@
class PythonClass:
- python_string = 'hello'
+ python_string = "hello"
python_integer = None
- LIST__python_list = ['a', 'b', 'c']
+ LIST__python_list = ["a", "b", "c"]
def __init__(self):
self.python_integer = 42
@@ -11,4 +11,4 @@ def python_method(self):
@property
def python_property(self):
- return 'value'
+ return "value"
diff --git a/atest/testdata/variables/automatic_variables/HelperLib.py b/atest/testdata/variables/automatic_variables/HelperLib.py
index 6e9809d53c1..c5c37deffa8 100644
--- a/atest/testdata/variables/automatic_variables/HelperLib.py
+++ b/atest/testdata/variables/automatic_variables/HelperLib.py
@@ -12,4 +12,4 @@ def import_time_value_should_be(self, name, expected):
if not isinstance(actual, str):
expected = eval(expected)
if actual != expected:
- raise AssertionError(f'{actual} != {expected}')
+ raise AssertionError(f"{actual} != {expected}")
diff --git a/atest/testdata/variables/automatic_variables/auto1.robot b/atest/testdata/variables/automatic_variables/auto1.robot
index 1870863b7a5..3bb7d9e0ac8 100644
--- a/atest/testdata/variables/automatic_variables/auto1.robot
+++ b/atest/testdata/variables/automatic_variables/auto1.robot
@@ -77,7 +77,7 @@ Suite Variables Are Available At Import Time
name Automatic Variables.Auto1
doc This is suite documentation. With \${VARIABLE}.
metadata {'MeTa1': 'Value', 'meta2': '\${VARIABLE}'}
- options {'include': ['include this test'], 'exclude': ['exclude', 'e2'], 'skip': ['skip_me'], 'skip_on_failure': ['sof']}
+ options {'rpa': False, 'include': ['include this test'], 'exclude': ['exclude', 'e2'], 'skip': ['skip_me'], 'skip_on_failure': ['sof'], 'console_width': 99}
Suite Status And Suite Message Are Not Visible In Tests
Variable Should Not Exist $SUITE_STATUS
@@ -123,3 +123,5 @@ Previous Test Variables Should Have Correct Values When That Test Fails
Should Contain ${OPTIONS.${name}} ${exp.upper()}
END
END
+ Should Be Equal ${OPTIONS.console_width} ${99}
+ Should Be Equal ${OPTIONS.rpa} ${False}
diff --git a/atest/testdata/variables/dict_vars.py b/atest/testdata/variables/dict_vars.py
index 73b6015bb83..4d730498f69 100644
--- a/atest/testdata/variables/dict_vars.py
+++ b/atest/testdata/variables/dict_vars.py
@@ -1,10 +1,12 @@
import os
-DICT_FROM_VAR_FILE = dict(a='1', b=2, c='3')
-ESCAPED_FROM_VAR_FILE = {'${a}': 'c:\\temp',
- 'b': '${2}',
- os.sep: '\n' if os.sep == '/' else '\r\n',
- '4=5\\=6': 'value'}
+DICT_FROM_VAR_FILE = dict(a="1", b=2, c="3")
+ESCAPED_FROM_VAR_FILE = {
+ "${a}": "c:\\temp",
+ "b": "${2}",
+ os.sep: "\n" if os.sep == "/" else "\r\n",
+ "4=5\\=6": "value",
+}
class ClassFromVarFile:
diff --git a/atest/testdata/variables/dynamic_variable_files/argument_conversion.py b/atest/testdata/variables/dynamic_variable_files/argument_conversion.py
index 32289a6131d..c5669f9585d 100644
--- a/atest/testdata/variables/dynamic_variable_files/argument_conversion.py
+++ b/atest/testdata/variables/dynamic_variable_files/argument_conversion.py
@@ -1,4 +1,4 @@
-def get_variables(string: str, number: 'int|float'):
+def get_variables(string: str, number: "int|float"):
assert isinstance(string, str)
assert isinstance(number, (int, float))
- return {'string': string, 'number': number}
+ return {"string": string, "number": number}
diff --git a/atest/testdata/variables/dynamic_variable_files/dyn_vars.py b/atest/testdata/variables/dynamic_variable_files/dyn_vars.py
index c44bafaa8dc..cd747bbe515 100644
--- a/atest/testdata/variables/dynamic_variable_files/dyn_vars.py
+++ b/atest/testdata/variables/dynamic_variable_files/dyn_vars.py
@@ -3,25 +3,27 @@
def get_variables(type):
- return {'dict': get_dict,
- 'mydict': MyDict,
- 'Mapping': get_MyMapping,
- 'UserDict': get_UserDict,
- 'MyUserDict': MyUserDict}[type]()
+ return {
+ "dict": get_dict,
+ "mydict": MyDict,
+ "Mapping": get_MyMapping,
+ "UserDict": get_UserDict,
+ "MyUserDict": MyUserDict,
+ }[type]()
def get_dict():
- return {'from dict': 'This From Dict', 'from dict2': 2}
+ return {"from dict": "This From Dict", "from dict2": 2}
class MyDict(dict):
def __init__(self):
- super().__init__(from_my_dict='This From My Dict', from_my_dict2=2)
+ super().__init__(from_my_dict="This From My Dict", from_my_dict2=2)
def get_MyMapping():
- data = {'from Mapping': 'This From Mapping', 'from Mapping2': 2}
+ data = {"from Mapping": "This From Mapping", "from Mapping2": 2}
class MyMapping(Mapping):
@@ -41,11 +43,12 @@ def __iter__(self):
def get_UserDict():
- return UserDict({'from UserDict': 'This From UserDict', 'from UserDict2': 2})
+ return UserDict({"from UserDict": "This From UserDict", "from UserDict2": 2})
class MyUserDict(UserDict):
def __init__(self):
- super().__init__({'from MyUserDict': 'This From MyUserDict',
- 'from MyUserDict2': 2})
+ super().__init__(
+ {"from MyUserDict": "This From MyUserDict", "from MyUserDict2": 2}
+ )
diff --git a/atest/testdata/variables/extended_assign.robot b/atest/testdata/variables/extended_assign.robot
index 3e524711063..d5a9546f3fe 100644
--- a/atest/testdata/variables/extended_assign.robot
+++ b/atest/testdata/variables/extended_assign.robot
@@ -2,6 +2,9 @@
Variables extended_assign_vars.py
Library Collections
+*** Variables ***
+&{DICT} key=value
+
*** Test Cases ***
Set attributes to Python object
[Setup] Should Be Equal ${VAR.attr}-${VAR.attr2} value-v2
@@ -25,19 +28,35 @@ Set item to list attribute
${body.data}[${0}] = Set Variable firstVal
${body.data}[-1] = Set Variable lastVal
${body.data}[1:3] = Create List ${98} middle ${99}
- ${EXPECTED_LIST} = Create List firstVal ${98} middle ${99} lastVal
- Lists Should Be Equal ${body.data} ${EXPECTED_LIST}
+ Lists Should Be Equal ${body.data} ${{['firstVal', 98, 'middle', 99, 'lastVal']}}
Set item to dict attribute
&{body} = Evaluate {'data': {'key': 'val', 0: 1}}
${body.data}[key] = Set Variable newVal
${body.data}[${0}] = Set Variable ${2}
${body.data}[newKey] = Set Variable newKeyVal
- ${EXPECTED_DICT} = Create Dictionary key=newVal ${0}=${2} newKey=newKeyVal
- Dictionaries Should Be Equal ${body.data} ${EXPECTED_DICT}
+ Dictionaries Should Be Equal ${body.data} ${{{'key': 'newVal', 0: 2, 'newKey': 'newKeyVal'}}}
+
+Set using @-syntax
+ [Documentation] FAIL Setting '\@{VAR.fail}' failed: Expected list-like value, got string.
+ @{DICT.key} = Create List 1 2 3
+ Should Be Equal ${DICT} ${{{'key': ['1', '2', '3']}}}
+ @{VAR.list: int} = Create List 1 2 3
+ Should Be Equal ${VAR.list} ${{[1, 2, 3]}}
+ @{VAR.fail} = Set Variable not a list
+
+Set using &-syntax
+ [Documentation] FAIL Setting '\&{DICT.fail}' failed: Expected dictionary-like value, got integer.
+ &{VAR.dict} = Create Dictionary key=value
+ Should Be Equal ${VAR.dict} ${{{'key': 'value'}}}
+ Should Be Equal ${VAR.dict.key} value
+ &{DICT.key: int=float} = Create Dictionary 1=2.3 ${4.0}=${5.6}
+ Should Be Equal ${DICT} ${{{'key': {1: 2.3, 4: 5.6}}}}
+ Should Be Equal ${DICT.key}[${1}] ${2.3}
+ &{DICT.fail} = Set Variable ${666}
Trying to set un-settable attribute
- [Documentation] FAIL STARTS: Setting attribute 'not_settable' to variable '\${VAR}' failed: AttributeError:
+ [Documentation] FAIL STARTS: Setting '\${VAR.not_settable}' failed: AttributeError:
${VAR.not_settable} = Set Variable whatever
Un-settable attribute error is catchable
@@ -45,11 +64,11 @@ Un-settable attribute error is catchable
... Teardown failed:
... Several failures occurred:
...
- ... 1) Setting attribute 'not_settable' to variable '\${VAR}' failed: AttributeError: *
+ ... 1) Setting '\${VAR.not_settable}' failed: AttributeError: *
...
... 2) AssertionError
Run Keyword And Expect Error
- ... Setting attribute 'not_settable' to variable '\${VAR}' failed: AttributeError: *
+ ... Setting '\${VAR.not_settable}' failed: AttributeError: *
... Setting unsettable attribute
[Teardown] Run Keywords Setting unsettable attribute Fail
@@ -78,11 +97,6 @@ Attribute name must be valid
Should Be Equal ${VAR.2nd} starts with number
Should Be Equal ${VAR.foo-bar} invalid char
-Extended syntax is ignored with list variables
- @{list} = Create List 1 2 3
- @{list.new} = Create List 1 2 3
- Should Be Equal ${list} ${list.new}
-
*** Keywords ***
Extended assignment is disabled
[Arguments] ${value}
diff --git a/atest/testdata/variables/extended_assign_vars.py b/atest/testdata/variables/extended_assign_vars.py
index 68e087c95f6..5d80b86e594 100644
--- a/atest/testdata/variables/extended_assign_vars.py
+++ b/atest/testdata/variables/extended_assign_vars.py
@@ -1,19 +1,23 @@
-__all__ = ['VAR']
+__all__ = ["VAR"]
class Demeter:
- loves = ''
+ loves = ""
+
@property
def hates(self):
return self.loves.upper()
class Variable:
- attr = 'value'
- _attr2 = 'v2'
- attr2 = property(lambda self: self._attr2,
- lambda self, value: setattr(self, '_attr2', value.upper()))
+ attr = "value"
+ _attr2 = "v2"
+ attr2 = property(
+ lambda self: self._attr2,
+ lambda self, value: setattr(self, "_attr2", value.upper()),
+ )
demeter = Demeter()
+
@property
def not_settable(self):
return None
diff --git a/atest/testdata/variables/extended_variables.py b/atest/testdata/variables/extended_variables.py
index 3f1f39998f6..a3344d5debd 100644
--- a/atest/testdata/variables/extended_variables.py
+++ b/atest/testdata/variables/extended_variables.py
@@ -1,20 +1,20 @@
class ExampleObject:
-
- def __init__(self, name=''):
+
+ def __init__(self, name=""):
self.name = name
def greet(self, name=None):
if not name:
- return '%s says hi!' % self.name
- if name == 'FAIL':
+ return f"{self.name} says hi!"
+ if name == "FAIL":
raise ValueError
- return '%s says hi to %s!' % (self.name, name)
-
+ return f"{self.name} says hi to {name}!"
+
def __str__(self):
return self.name
-
+
def __repr__(self):
- return "'%s'" % self.name
+ return repr(self.name)
-OBJ = ExampleObject('dude')
+OBJ = ExampleObject("dude")
diff --git a/atest/testdata/variables/get_file_lib.py b/atest/testdata/variables/get_file_lib.py
index c9a9738e5ce..ac6a60bb4aa 100644
--- a/atest/testdata/variables/get_file_lib.py
+++ b/atest/testdata/variables/get_file_lib.py
@@ -1,2 +1,2 @@
def get_open_file():
- return open(__file__)
\ No newline at end of file
+ return open(__file__, encoding="ASCII")
diff --git a/atest/testdata/variables/list_and_dict_variable_file.py b/atest/testdata/variables/list_and_dict_variable_file.py
index af5ba6d42d8..4a0e96c201c 100644
--- a/atest/testdata/variables/list_and_dict_variable_file.py
+++ b/atest/testdata/variables/list_and_dict_variable_file.py
@@ -3,35 +3,37 @@
def get_variables(*args):
if args:
- return dict((args[i], args[i+1]) for i in range(0, len(args), 2))
- list_ = ['1', '2', 3]
+ return {args[i]: args[i + 1] for i in range(0, len(args), 2)}
+ list_ = ["1", "2", 3]
tuple_ = tuple(list_)
- dict_ = {'a': 1, 2: 'b', 'nested': {'key': 'value'}}
+ dict_ = {"a": 1, 2: "b", "nested": {"key": "value"}}
ordered = OrderedDict((chr(o), o) for o in range(97, 107))
- open_file = open(__file__)
- closed_file = open(__file__)
+ open_file = open(__file__, encoding="UTF-8")
+ closed_file = open(__file__, "rb")
closed_file.close()
- return {'LIST__list': list_,
- 'LIST__tuple': tuple_,
- 'LIST__generator': (i for i in range(5)),
- 'DICT__dict': dict_,
- 'DICT__ordered': ordered,
- 'scalar_list': list_,
- 'scalar_tuple': tuple_,
- 'scalar_generator': (i for i in range(5)),
- 'scalar_dict': dict_,
- 'failing_generator': failing_generator,
- 'failing_dict': FailingDict({1: 2}),
- 'open_file': open_file,
- 'closed_file': closed_file}
+ return {
+ "LIST__list": list_,
+ "LIST__tuple": tuple_,
+ "LIST__generator": (i for i in range(5)),
+ "DICT__dict": dict_,
+ "DICT__ordered": ordered,
+ "scalar_list": list_,
+ "scalar_tuple": tuple_,
+ "scalar_generator": (i for i in range(5)),
+ "scalar_dict": dict_,
+ "failing_generator": failing_generator,
+ "failing_dict": FailingDict({1: 2}),
+ "open_file": open_file,
+ "closed_file": closed_file,
+ }
def failing_generator():
for i in [2, 1, 0]:
- yield 1/i
+ yield 1 / i
class FailingDict(dict):
def __getattribute__(self, item):
- raise Exception('Bang')
+ raise Exception("Bang")
diff --git a/atest/testdata/variables/list_variable_items.py b/atest/testdata/variables/list_variable_items.py
index 9ef3a7d3093..4df8b4b2ea0 100644
--- a/atest/testdata/variables/list_variable_items.py
+++ b/atest/testdata/variables/list_variable_items.py
@@ -1,11 +1,11 @@
def get_variables():
- return {'MIXED USAGE': MixedUsage()}
+ return {"MIXED USAGE": MixedUsage()}
class MixedUsage:
def __init__(self):
- self.data = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K']
+ self.data = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K"]
def __getitem__(self, item):
if isinstance(item, slice) and item.start is item.stop is item.step is None:
diff --git a/atest/testdata/variables/non_string_variables.py b/atest/testdata/variables/non_string_variables.py
index 4e513c60203..2c90acc90f0 100644
--- a/atest/testdata/variables/non_string_variables.py
+++ b/atest/testdata/variables/non_string_variables.py
@@ -2,17 +2,19 @@
def get_variables():
- variables = {'integer': 42,
- 'float': 3.14,
- 'byte_string': b'hyv\xe4',
- 'byte_string_str': 'hyv\\xe4',
- 'boolean': True,
- 'none': None,
- 'module': sys,
- 'module_str': str(sys),
- 'list': [1, b'\xe4', '\xe4'],
- 'dict': {b'\xe4': '\xe4'},
- 'list_str': u"[1, b'\\xe4', '\xe4']",
- 'dict_str': u"{b'\\xe4': '\xe4'}"}
+ variables = {
+ "integer": 42,
+ "float": 3.14,
+ "bytes": b"hyv\xe4",
+ "bytearray": bytearray(b"hyv\xe4"),
+ "low_bytes": b"\x00\x01\x02",
+ "boolean": True,
+ "none": None,
+ "module": sys,
+ "module_str": str(sys),
+ "list": [1, b"\xe4", "\xe4"],
+ "dict": {b"\xe4": "\xe4"},
+ "list_str": "[1, b'\\xe4', '\xe4']",
+ "dict_str": "{b'\\xe4': '\xe4'}",
+ }
return variables
-
diff --git a/atest/testdata/variables/non_string_variables.robot b/atest/testdata/variables/non_string_variables.robot
index 691e286c5ec..f090098e2ff 100644
--- a/atest/testdata/variables/non_string_variables.robot
+++ b/atest/testdata/variables/non_string_variables.robot
@@ -10,10 +10,26 @@ Numbers
${FLOAT} ${3.14}
-${FLOAT}- -3.14-
-Byte string
- [Documentation] We has ${BYTE STRING}!
- ${BYTE STRING} ${BYTE STRING}
- -${BYTE STRING}- -${BYTE STRING STR}-
+Bytes
+ [Documentation] We has ${BYTES}!
+ ${BYTES} ${BYTES}
+ ${BYTEARRAY} ${BYTEARRAY}
+ ${LOW BYTES} ${LOW BYTES}
+ -${BYTES}- -hyvä-
+ -${BYTEARRAY}- -hyvä-
+ -${LOW BYTES}- -\x00\x01\x02-
+
+Bytes concatenated with bytes yields bytes
+ [Documentation] ${BYTES}${BYTEARRAY}
+ ${BYTES}${BYTEARRAY} ${{b'hyv\xe4hyv\xe4'}}
+ ${BYTEARRAY}${BYTES} ${{b'hyv\xe4hyv\xe4'}}
+ ${BYTES}${{b'-'}}${BYTES} ${{b'hyv\xe4-hyv\xe4'}}
+
+Bytes string representation can be converted back to bytes
+ [Documentation] ${LOW BYTES}
+ [Template] NONE
+ ${result} = Convert To Bytes ${BYTES}, ${LOW BYTES} and ${BYTEARRAY}
+ Should Be Equal ${result} ${{b'hyv\xe4, \x00\x01\x02 and hyv\xe4'}}
Collections
[Documentation] ${LIST} ${DICT}
diff --git a/atest/testdata/variables/resvarfiles/cli_vars.py b/atest/testdata/variables/resvarfiles/cli_vars.py
index 0a0a4bb5aff..3491eee018c 100644
--- a/atest/testdata/variables/resvarfiles/cli_vars.py
+++ b/atest/testdata/variables/resvarfiles/cli_vars.py
@@ -1,6 +1,6 @@
-SCALAR = 'Scalar from variable file from CLI'
-SCALAR_WITH_ESCAPES = r'1 \ 2\\ ${inv}'
-SCALAR_LIST = 'List variable value'.split()
+SCALAR = "Scalar from variable file from CLI"
+SCALAR_WITH_ESCAPES = r"1 \ 2\\ ${inv}"
+SCALAR_LIST = "List variable value".split()
LIST__LIST = SCALAR_LIST
-PRIORITIES_1 = PRIORITIES_2 = 'Variable File from CLI'
+PRIORITIES_1 = PRIORITIES_2 = "Variable File from CLI"
diff --git a/atest/testdata/variables/resvarfiles/cli_vars_2.py b/atest/testdata/variables/resvarfiles/cli_vars_2.py
index 3dec99a9e64..bd78f5d6381 100644
--- a/atest/testdata/variables/resvarfiles/cli_vars_2.py
+++ b/atest/testdata/variables/resvarfiles/cli_vars_2.py
@@ -1,12 +1,13 @@
-def get_variables(name, value='default value', conversion: int = 0):
- if name == 'FAIL':
- 1/0
+def get_variables(name, value="default value", conversion: int = 0):
+ if name == "FAIL":
+ 1 / 0
assert isinstance(conversion, int)
- varz = {name: value,
- 'ANOTHER_SCALAR': 'Variable from CLI var file with get_variables',
- 'LIST__ANOTHER_LIST': ['List variable from CLI var file',
- 'with get_variables'],
- 'CONVERSION': conversion}
- for name in 'PRIORITIES_1', 'PRIORITIES_2', 'PRIORITIES_2B':
- varz[name] = 'Second Variable File from CLI'
+ varz = {
+ name: value,
+ "ANOTHER_SCALAR": "Variable from CLI var file with get_variables",
+ "LIST__ANOTHER_LIST": ["List variable from CLI var file", "with get_variables"],
+ "CONVERSION": conversion,
+ }
+ for name in "PRIORITIES_1", "PRIORITIES_2", "PRIORITIES_2B":
+ varz[name] = "Second Variable File from CLI"
return varz
diff --git a/atest/testdata/variables/resvarfiles/pythonpath_dir/package/submodule.py b/atest/testdata/variables/resvarfiles/pythonpath_dir/package/submodule.py
index 641523e55de..25b74101670 100644
--- a/atest/testdata/variables/resvarfiles/pythonpath_dir/package/submodule.py
+++ b/atest/testdata/variables/resvarfiles/pythonpath_dir/package/submodule.py
@@ -1 +1 @@
-VARIABLE_IN_SUBMODULE = 'VALUE IN SUBMODULE'
+VARIABLE_IN_SUBMODULE = "VALUE IN SUBMODULE"
diff --git a/atest/testdata/variables/resvarfiles/pythonpath_dir/pythonpath_varfile.py b/atest/testdata/variables/resvarfiles/pythonpath_dir/pythonpath_varfile.py
index 56d91670356..55ca89f072f 100644
--- a/atest/testdata/variables/resvarfiles/pythonpath_dir/pythonpath_varfile.py
+++ b/atest/testdata/variables/resvarfiles/pythonpath_dir/pythonpath_varfile.py
@@ -1,3 +1,5 @@
def get_variables(*args):
- return {'PYTHONPATH VAR %d' % len(args): 'Varfile found from PYTHONPATH',
- 'PYTHONPATH ARGS %d' % len(args): '-'.join(args)}
+ return {
+ f"PYTHONPATH VAR {len(args)}": "Varfile found from PYTHONPATH",
+ f"PYTHONPATH ARGS {len(args)}": "-".join(args),
+ }
diff --git a/atest/testdata/variables/resvarfiles/variables.py b/atest/testdata/variables/resvarfiles/variables.py
index e6a05672f69..7debc6370c6 100644
--- a/atest/testdata/variables/resvarfiles/variables.py
+++ b/atest/testdata/variables/resvarfiles/variables.py
@@ -9,29 +9,30 @@ def __repr__(self):
return repr(self.name)
-STRING = 'Hello world!'
+STRING = "Hello world!"
INTEGER = 42
FLOAT = -1.2
BOOLEAN = True
NONE_VALUE = None
-ESCAPES = 'one \\ two \\\\ ${non_existing}'
-NO_VALUE = ''
-LIST = ['Hello', 'world', '!']
+ESCAPES = "one \\ two \\\\ ${non_existing}"
+NO_VALUE = ""
+LIST = ["Hello", "world", "!"]
LIST_WITH_NON_STRINGS = [42, -1.2, True, None]
-LIST_WITH_ESCAPES = ['one \\', 'two \\\\', 'three \\\\\\', '${non_existing}']
-OBJECT = _Object('dude')
+LIST_WITH_ESCAPES = ["one \\", "two \\\\", "three \\\\\\", "${non_existing}"]
+OBJECT = _Object("dude")
-LIST__ONE_ITEM = ['Hello again?']
-LIST__LIST_2 = ['Hello', 'again', '?']
+LIST__ONE_ITEM = ["Hello again?"]
+LIST__LIST_2 = ["Hello", "again", "?"]
LIST__LIST_WITH_ESCAPES_2 = LIST_WITH_ESCAPES[:]
LIST__EMPTY_LIST = []
LIST__OBJECTS = [STRING, INTEGER, LIST, OBJECT]
-lowercase = 'Variable name in lower case'
+lowercase = "Variable name in lower case"
LIST__lowercase_list = [lowercase]
-Und_er__scores_____ = 'Variable name with under scores'
+Und_er__scores_____ = "Variable name with under scores"
LIST________UN__der__SCO__r_e_s__liST__ = [Und_er__scores_____]
-PRIORITIES_1 = PRIORITIES_2 = PRIORITIES_3 = PRIORITIES_4 = PRIORITIES_4B \
- = 'Variable File'
+PRIORITIES_1 = PRIORITIES_2 = PRIORITIES_3 = PRIORITIES_4 = PRIORITIES_4B = (
+ "Variable File"
+)
diff --git a/atest/testdata/variables/resvarfiles/variables_2.py b/atest/testdata/variables/resvarfiles/variables_2.py
index 7f73f922637..ed711a9e01b 100644
--- a/atest/testdata/variables/resvarfiles/variables_2.py
+++ b/atest/testdata/variables/resvarfiles/variables_2.py
@@ -1,2 +1,3 @@
-PRIORITIES_1 = PRIORITIES_2 = PRIORITIES_3 = PRIORITIES_4 = PRIORITIES_4B \
- = PRIORITIES_4C = 'Second Variable File'
+PRIORITIES_1 = PRIORITIES_2 = PRIORITIES_3 = PRIORITIES_4 = PRIORITIES_4B = (
+ PRIORITIES_4C
+) = "Second Variable File"
diff --git a/atest/testdata/variables/return_values.py b/atest/testdata/variables/return_values.py
index 46ee055eec5..37e2f639a55 100644
--- a/atest/testdata/variables/return_values.py
+++ b/atest/testdata/variables/return_values.py
@@ -15,9 +15,11 @@ def __getitem__(self, item):
def container(self):
return self._dict
+
class ObjectWithoutSetItemCap:
def __init__(self) -> None:
pass
+
OBJECT_WITH_SETITEM_CAP = ObjectWithSetItemCap()
OBJECT_WITHOUT_SETITEM_CAP = ObjectWithoutSetItemCap()
diff --git a/atest/testdata/variables/return_values.robot b/atest/testdata/variables/return_values.robot
index ddb61c02173..d86f0a3e297 100644
--- a/atest/testdata/variables/return_values.robot
+++ b/atest/testdata/variables/return_values.robot
@@ -120,8 +120,10 @@ Only One List Variable Allowed 1
@{list} @{list2} = Fail Not executed
Only One List Variable Allowed 2
- [Documentation] FAIL Assignment can contain only one list variable.
- @{list} ${scalar} @{list2} = Fail Not executed
+ [Documentation] FAIL Multiple errors:
+ ... - Assign mark '=' can be used only with the last variable.
+ ... - Assignment can contain only one list variable.
+ @{list} ${scalar} = @{list2} = Fail Not executed
List After Scalars
${first} @{rest} = Evaluate range(5)
@@ -209,8 +211,10 @@ Dictionary only allowed alone 3
&{d} @{l} = Fail Not executed
Dictionary only allowed alone 4
- [Documentation] FAIL Dictionary variable cannot be assigned with other variables.
- @{l} &{d} = Fail Not executed
+ [Documentation] FAIL Multiple errors:
+ ... - Assign mark '=' can be used only with the last variable.
+ ... - Dictionary variable cannot be assigned with other variables.
+ @{l}= &{d} = Fail Not executed
Dictionary only allowed alone 5
[Documentation] FAIL Dictionary variable cannot be assigned with other variables.
diff --git a/atest/testdata/variables/same_variable_file_names/different_variable_files/suite1/variable.py b/atest/testdata/variables/same_variable_file_names/different_variable_files/suite1/variable.py
index 13c53577bde..82c51b63499 100644
--- a/atest/testdata/variables/same_variable_file_names/different_variable_files/suite1/variable.py
+++ b/atest/testdata/variables/same_variable_file_names/different_variable_files/suite1/variable.py
@@ -1,2 +1 @@
SUITE = SUITE_1 = "suite1"
-
diff --git a/atest/testdata/variables/scalar_lists.py b/atest/testdata/variables/scalar_lists.py
index 35bb90fd092..12ce3feb6a2 100644
--- a/atest/testdata/variables/scalar_lists.py
+++ b/atest/testdata/variables/scalar_lists.py
@@ -1,12 +1,14 @@
-LIST = ['spam', 'eggs', 21]
+LIST = ["spam", "eggs", 21]
class _Extended:
list = LIST
- string = 'not a list'
+ string = "not a list"
+
def __getitem__(self, item):
return LIST
+
EXTENDED = _Extended()
@@ -14,4 +16,5 @@ class _Iterable:
def __iter__(self):
return iter(LIST)
+
ITERABLE = _Iterable()
diff --git a/atest/testdata/variables/var_syntax/__init__.robot b/atest/testdata/variables/var_syntax/__init__.robot
new file mode 100644
index 00000000000..06cf439606b
--- /dev/null
+++ b/atest/testdata/variables/var_syntax/__init__.robot
@@ -0,0 +1,22 @@
+*** Settings ***
+Suite Setup VAR in suite setup and teardown root suite setup
+Suite Teardown VAR in suite setup and teardown root suite teardown
+
+*** Keywords ***
+VAR in suite setup and teardown
+ [Arguments] ${where}
+ VAR ${local} value
+ VAR ${SUITE} set in ${where} scope=suite
+ VAR ${SUITES} set in ${where} scope=suites
+ VAR ${GLOBAL} set in ${where} scope=global
+ VAR ${ROOT} set in ${where} scope=global
+ Should Be Equal ${local} value
+ Should Be Equal ${SUITE} set in ${where}
+ Should Be Equal ${GLOBAL} set in ${where}
+ IF $where == 'root suite setup'
+ VAR ${TEST} set in ${where} scope=test
+ ELSE
+ Should Be Equal ${TEST} set in root suite setup
+ VAR ${TEST} set in ${where}
+ Should Be Equal ${TEST} set in root suite teardown
+ END
diff --git a/atest/testdata/variables/var_syntax.robot b/atest/testdata/variables/var_syntax/suite1.robot
similarity index 72%
rename from atest/testdata/variables/var_syntax.robot
rename to atest/testdata/variables/var_syntax/suite1.robot
index 12725a62c19..48d9f0592a9 100644
--- a/atest/testdata/variables/var_syntax.robot
+++ b/atest/testdata/variables/var_syntax/suite1.robot
@@ -1,6 +1,6 @@
*** Settings ***
-Suite Setup VAR in suite setup and teardown suite setup
-Suite Teardown VAR in suite setup and teardown suite teardown
+Suite Setup VAR in suite setup and teardown suite1 setup
+Suite Teardown VAR in suite setup and teardown suite1 teardown
*** Test Cases ***
Scalar
@@ -12,7 +12,7 @@ Scalar with separator
VAR ${b} 1 ${2} 3 separator====
VAR ${c} 1 2 ${3} separator=
VAR ${d} ${a} ${b} ${c} separator=${0}
- VAR ${e} separator=has no effect
+ VAR ${e} separator=no effect
VAR ${f} separator=NO separator=NO separator=--YES--
Should Be Equal ${a} 1\n2\n3
Should Be Equal ${b} 1===2===3
@@ -29,6 +29,21 @@ Dict
VAR &{name} k1=v1 k2=v2 separator=v3
Should Be Equal ${name} ${{{'k1': 'v1', 'k2': 'v2', 'separator': 'v3'}}}
+Long values
+ ${items} = Create List
+ ... This is a rather long value.
+ ... It will be cut when it is logged by VAR.
+ ... Otherwise it should work normally.
+ ... This is a rather long value.
+ ... It will be cut when it is logged by VAR.
+ ... Otherwise it should work normally.
+ VAR ${scalar} @{items}
+ VAR @{list} @{items}
+ VAR &{dict} &{{dict(enumerate($items))}}
+ Should Be Equal ${scalar} ${{' '.join($items)}}
+ Should Be Equal ${list} ${items}
+ Should Be Equal ${dict} ${{dict(enumerate($items))}}
+
Invalid name
[Documentation] FAIL Invalid variable name 'bad'.
VAR bad name
@@ -50,20 +65,27 @@ Equals is accepted
VAR &{name}= k1=v1 k2=v2
Should Be Equal ${name} ${{{'k1': 'v1', 'k2': 'v2'}}}
+In root suite setup
+ Should Be Equal ${ROOT} set in root suite setup
+
In suite setup
- Should Be Equal ${SUITE} set in suite setup
- Should Be Equal ${GLOBAL} set in suite setup
+ Variable Should Not Exist ${TEST}
+ Should Be Equal ${SUITE} set in suite1 setup
+ Should Be Equal ${SUITES} set in suite1 setup
+ Should Be Equal ${GLOBAL} set in suite1 setup
Scopes 1
VAR ${local1} local1
VAR ${local2} scope=local2 scope=LOCAL
VAR @{TEST} scope=value scope=test
VAR &{SUITE} scope=value scope=${{'suite'}}
+ VAR ${SUITES} children too scope=Suites
VAR ${GLOBAL} global scope=GLOBAL
Should Be Equal ${local1} local1
Should Be Equal ${local2} scope=local2
Should Be Equal ${TEST} ${{['scope=value']}}
Should Be Equal ${SUITE} ${{{'scope': 'value'}}}
+ Should Be Equal ${SUITES} children too
Should Be Equal ${GLOBAL} global
Scopes
Should Be Equal ${TEST} new-test
@@ -73,14 +95,16 @@ Scopes 2
Variable Should Not Exist ${local1}
Variable Should Not Exist ${local2}
Should Be Equal ${SUITE} ${{{'scope': 'value'}}}
+ Should Be Equal ${SUITES} children too
Should Be Equal ${GLOBAL} global
+ Should Be Equal ${ROOT} set in root suite setup
Invalid scope
- [Documentation] FAIL VAR option 'scope' does not accept value 'invalid'. Valid values are 'GLOBAL', 'SUITE', 'TEST', 'TASK' and 'LOCAL'.
+ [Documentation] FAIL VAR option 'scope' does not accept value 'invalid'. Valid values are 'LOCAL', 'TEST', 'TASK', 'SUITE', 'SUITES' and 'GLOBAL'.
VAR ${x} x scope=invalid
Invalid scope from variable
- [Documentation] FAIL Invalid VAR scope: Value 'invalid' is not accepted. Valid values are 'GLOBAL', 'SUITE', 'TEST', 'TASK' and 'LOCAL'.
+ [Documentation] FAIL Invalid VAR scope: Value 'invalid' is not accepted. Valid values are 'LOCAL', 'TEST', 'TASK', 'SUITE', 'SUITES' and 'GLOBAL'.
VAR ${x} x scope=${{'invalid'}}
Non-existing variable as scope
@@ -181,6 +205,7 @@ With TRY
*** Keywords ***
Scopes
+ Variable Should Not Exist ${local}
Variable Should Not Exist ${local1}
Variable Should Not Exist ${local2}
Should Be Equal ${TEST} ${{['scope=value']}}
@@ -193,14 +218,30 @@ Scopes
VAR in suite setup and teardown
[Arguments] ${where}
+ Variable Should Not Exist ${local}
+ Should Be Equal ${ROOT} set in root suite setup
+ IF 'setup' in $where
+ Variable Should Not Exist ${SUITE}
+ Should Be Equal ${SUITES} set in root suite setup
+ Should Be Equal ${GLOBAL} set in root suite setup
+ ELSE
+ Should Be Equal ${SUITE} ${{{'scope': 'value'}}}
+ Should Be Equal ${SUITES} children too
+ Should Be Equal ${GLOBAL} global
+ END
VAR ${local} value
VAR ${SUITE} set in ${where} scope=suite
+ VAR ${SUITES} set in ${where} scope=suites
VAR ${GLOBAL} set in ${where} scope=global
Should Be Equal ${local} value
Should Be Equal ${SUITE} set in ${where}
+ Should Be Equal ${SUITES} set in ${where}
Should Be Equal ${GLOBAL} set in ${where}
- TRY
- VAR ${TEST} this fails scope=test
- EXCEPT AS ${err}
- Should Be Equal ${err} Setting variable '\${TEST}' failed: Cannot set test variable when no test is started.
+ IF $where == 'suite1 setup'
+ Variable Should Not Exist ${TEST}
+ VAR ${TEST} set in ${where} scope=test
+ ELSE
+ Should Be Equal ${TEST} set in suite1 setup
+ VAR ${TEST} set in ${where}
+ Should Be Equal ${TEST} set in suite1 teardown
END
diff --git a/atest/testdata/variables/var_syntax/suite2.robot b/atest/testdata/variables/var_syntax/suite2.robot
new file mode 100644
index 00000000000..9b8a3d09034
--- /dev/null
+++ b/atest/testdata/variables/var_syntax/suite2.robot
@@ -0,0 +1,7 @@
+*** Test Cases ***
+Scopes 3
+ Variable Should Not Exist ${TEST}
+ Variable Should Not Exist ${SUITE}
+ Should Be Equal ${SUITES} set in root suite setup # set in root, changes in suite1 not seen here
+ Should Be Equal ${GLOBAL} set in suite1 teardown # set in root, changed in suite2
+ Should Be Equal ${ROOT} set in root suite setup # set in root, not changed
diff --git a/atest/testdata/variables/variable_recommendation_vars.py b/atest/testdata/variables/variable_recommendation_vars.py
index 31a7c54af13..ebfbf50ddc6 100644
--- a/atest/testdata/variables/variable_recommendation_vars.py
+++ b/atest/testdata/variables/variable_recommendation_vars.py
@@ -1,6 +1,6 @@
class ExampleObject:
- def __init__(self, name=''):
+ def __init__(self, name=""):
self.name = name
-OBJ = ExampleObject('dude')
+OBJ = ExampleObject("dude")
diff --git a/atest/testdata/variables/variable_recommendations.robot b/atest/testdata/variables/variable_recommendations.robot
index e429cc8c87b..246d4a8132a 100644
--- a/atest/testdata/variables/variable_recommendations.robot
+++ b/atest/testdata/variables/variable_recommendations.robot
@@ -20,140 +20,135 @@ ${S DICTIONARY} Not recommended as dict
*** Test Cases ***
Simple Typo Scalar
- [Documentation] FAIL Variable '${SSTRING}' not found. Did you mean:
+ [Documentation] FAIL Variable '\${SSTRING}' not found. Did you mean:
... ${INDENT}\${STRING}
Log ${SSTRING}
Simple Typo List - Only List-likes Are Recommended
- [Documentation] FAIL Variable '@{GIST}' not found. Did you mean:
+ [Documentation] FAIL Variable '\@{GIST}' not found. Did you mean:
... ${INDENT}\@{LIST}
... ${INDENT}\@{D LIST}
Log @{GIST}
Simple Typo Dict - Only Dicts Are Recommended
- [Documentation] FAIL Variable '&{BICTIONARY}' not found. Did you mean:
+ [Documentation] FAIL Variable '\&{BICTIONARY}' not found. Did you mean:
... ${INDENT}\&{DICTIONARY}
Log &{BICTIONARY}
All Types Are Recommended With Scalars 1
- [Documentation] FAIL Variable '${MIST}' not found. Did you mean:
+ [Documentation] FAIL Variable '\${MIST}' not found. Did you mean:
... ${INDENT}\${LIST}
... ${INDENT}\${S LIST}
... ${INDENT}\${D LIST}
Log ${MIST}
All Types Are Recommended With Scalars 2
- [Documentation] FAIL Variable '${BICTIONARY}' not found. Did you mean:
+ [Documentation] FAIL Variable '\${BICTIONARY}' not found. Did you mean:
... ${INDENT}\${DICTIONARY}
... ${INDENT}\${S DICTIONARY}
... ${INDENT}\${L DICTIONARY}
Log ${BICTIONARY}
Access Scalar In List With Typo In Variable
- [Documentation] FAIL Variable '@{LLIST}' not found. Did you mean:
+ [Documentation] FAIL Variable '\@{LLIST}' not found. Did you mean:
... ${INDENT}\@{LIST}
... ${INDENT}\@{D LIST}
Log @{LLIST}[0]
Access Scalar In List With Typo In Index
- [Documentation] FAIL Variable '${STRENG}' not found. Did you mean:
+ [Documentation] FAIL Variable '\${STRENG}' not found. Did you mean:
... ${INDENT}\${STRING}
Log @{LIST}[${STRENG}]
Long Garbage Variable
- [Documentation] FAIL Variable '${dEnOKkgGlYBHwotU2bifJ56w487jD2NJxCrcM62g}' not found.
+ [Documentation] FAIL Variable '\${dEnOKkgGlYBHwotU2bifJ56w487jD2NJxCrcM62g}' not found.
Log ${dEnOKkgGlYBHwotU2bifJ56w487jD2NJxCrcM62g}
Many Similar Variables
- [Documentation] FAIL Variable '${SIMILAR VAR}' not found. Did you mean:
+ [Documentation] FAIL Variable '\${SIMILAR VAR}' not found. Did you mean:
... ${INDENT}\${SIMILAR VAR 3}
... ${INDENT}\${SIMILAR VAR 2}
... ${INDENT}\${SIMILAR VAR 1}
Log ${SIMILAR VAR}
Misspelled Lower Case
- [Documentation] FAIL Variable '${sstring}' not found. Did you mean:
+ [Documentation] FAIL Variable '\${sstring}' not found. Did you mean:
... ${INDENT}\${STRING}
Log ${sstring}
Misspelled Underscore
- [Documentation] FAIL Variable '${_S_STRI_NG}' not found. Did you mean:
+ [Documentation] FAIL Variable '\${_S_STRI_NG}' not found. Did you mean:
... ${INDENT}\${STRING}
Log ${_S_STRI_NG}
Misspelled Period
- [Documentation] FAIL Resolving variable '${INT.EGER}' failed: Variable '${INT}' not found. Did you mean:
+ [Documentation] FAIL Resolving variable '\${INT.EGER}' failed: Variable '\${INT}' not found. Did you mean:
... ${INDENT}\${INDENT}
... ${INDENT}\${INTEGER}
Log ${INT.EGER}
Misspelled Camel Case
- [Documentation] FAIL Variable '@{OneeItem}' not found. Did you mean:
+ [Documentation] FAIL Variable '\@{OneeItem}' not found. Did you mean:
... ${INDENT}\@{ONE ITEM}
Log @{OneeItem}
Misspelled Whitespace
- [Documentation] FAIL Variable '${S STRI NG}' not found. Did you mean:
+ [Documentation] FAIL Variable '\${S STRI NG}' not found. Did you mean:
... ${INDENT}\${STRING}
Log ${S STRI NG}
Misspelled Env Var
- [Documentation] FAIL Environment variable '%{THISS_ENV_VAR_IS_SET}' not found. Did you mean:
+ [Documentation] FAIL Environment variable '\%{THISS_ENV_VAR_IS_SET}' not found. Did you mean:
... ${INDENT}\%{THIS_ENV_VAR_IS_SET}
Set Environment Variable THIS_ENV_VAR_IS_SET Env var value
${THISS_ENV_VAR_IS_SET} = Set Variable Not env var and thus not recommended
Log %{THISS_ENV_VAR_IS_SET}
Misspelled Env Var With Internal Variables
- [Documentation] FAIL Environment variable '%{YET_ANOTHER_ENV_VAR}' not found. Did you mean:
+ [Documentation] FAIL Environment variable '\%{YET_ANOTHER_ENV_VAR}' not found. Did you mean:
... ${INDENT}\%{ANOTHER_ENV_VAR}
Set Environment Variable ANOTHER_ENV_VAR ANOTHER_ENV_VAR
Log %{YET_%{ANOTHER_ENV_VAR}}
-Misspelled List Variable With Period
- [Documentation] FAIL Resolving variable '${list.nnew}' failed: AttributeError: 'list' object has no attribute 'nnew'
- @{list.new} = Create List 1 2 3
- Log ${list.nnew}
-
Misspelled Extended Variable Parent
- [Documentation] FAIL Resolving variable '${OBJJ.name}' failed: Variable '${OBJJ}' not found. Did you mean:
+ [Documentation] FAIL Resolving variable '\${OBJJ.name}' failed: Variable '${OBJJ}' not found. Did you mean:
... ${INDENT}\${OBJ}
Log ${OBJJ.name}
Misspelled Extended Variable Parent As List
[Documentation] Extended variables are always searched as scalars.
- ... FAIL Resolving variable '@{OBJJ.name}' failed: Variable '${OBJJ}' not found. Did you mean:
+ ... FAIL Resolving variable '\@{OBJJ.name}' failed: Variable '\${OBJJ}' not found. Did you mean:
... ${INDENT}\${OBJ}
Log @{OBJJ.name}
Misspelled Extended Variable Child
- [Documentation] FAIL Resolving variable '${OBJ.nmame}' failed: AttributeError: 'ExampleObject' object has no attribute 'nmame'
+ [Documentation] FAIL Resolving variable '\${OBJ.nmame}' failed: AttributeError: 'ExampleObject' object has no attribute 'nmame'
Log ${OBJ.nmame}
Existing Non ASCII Variable Name
- [Documentation] FAIL Variable '${Ceärsŵs}' not found. Did you mean:
+ [Documentation] FAIL Variable '\${Ceärsŵs}' not found. Did you mean:
... ${INDENT}\${Cäersŵs}
Log ${Ceärsŵs}
Non Existing Non ASCII Variable Name
- [Documentation] FAIL Variable '${ノಠ益ಠノ}' not found.
+ [Documentation] FAIL Variable '\${ノಠ益ಠノ}' not found.
Log ${ノಠ益ಠノ}
Invalid Binary
- [Documentation] FAIL Variable '${0b123}' not found.
+ [Documentation] FAIL Variable '\${0b123}' not found.
Log ${0b123}
Invalid Multiple Whitespace
- [Documentation] FAIL Resolving variable '${SPACVE * 5}' failed: Variable '${SPACVE }' not found. Did you mean:
+ [Documentation] FAIL Resolving variable '\${SPACVE * 5}' failed: Variable '\${SPACVE }' not found. Did you mean:
... ${INDENT}\${SPACE}
Log ${SPACVE * 5}
Non Existing Env Var
- [Documentation] FAIL Environment variable '%{THIS_ENV_VAR_DOES_NOT_EXIST}' not found.
+ [Documentation] FAIL Environment variable '\%{THIS_ENV_VAR_DOES_NOT_EXIST}' not found.
Log %{THIS_ENV_VAR_DOES_NOT_EXIST}
Multiple Missing Variables
- [Documentation] FAIL Variable '${SSTRING}' not found. Did you mean:
+ [Documentation] FAIL Variable '\${SSTRING}' not found. Did you mean:
... ${INDENT}\${STRING}
Log Many ${SSTRING} @{LLIST}
@@ -162,7 +157,7 @@ Empty Variable Name
Log ${}
Environment Variable With Misspelled Internal Variables
- [Documentation] FAIL Variable '${nnormal_var}' not found. Did you mean:
+ [Documentation] FAIL Variable '\${nnormal_var}' not found. Did you mean:
... ${INDENT}\${normal_var}
Set Environment Variable yet_another_env_var THIS_ENV_VAR
${normal_var} = Set Variable IS_SET
diff --git a/atest/testdata/variables/variable_section.robot b/atest/testdata/variables/variable_section.robot
index 179509f87b1..e23815dc785 100644
--- a/atest/testdata/variables/variable_section.robot
+++ b/atest/testdata/variables/variable_section.robot
@@ -1,3 +1,6 @@
+*** Settings ***
+Documentation Dictionary variables are tested in a separate suite.
+
*** Variables ***
${STRING} Hello world!
${INTEGER} ${42}
@@ -13,6 +16,10 @@ ${NO VALUE} ${EMPTY}
@{LIST CREATED FROM LIST WITH ESCAPES} @{LIST WITH ESCAPES}
@{SPACE ESC LIST} \ lead trail \ \ \ 2 \ \ \ \ \ 3 \ \ \
@{EMPTY LIST}
+${MUTABLE} ${{[]}}
+&{DICT} dicts=are mostly tested elsewhere
+@{AAA MUTABLE ITEMS} ${MUTABLE} ${LIST} ${DICT}
+@{ZZZ MUTABLE ITEMS} ${MUTABLE} ${LIST} ${DICT}
${lowercase} Variable name in lower case
@{lowercaselist} Variable name in lower case
${S P a c e s } Variable name with spaces
@@ -95,6 +102,16 @@ List With No Items
${ret} = Catenate @{EMPTY LIST} @{EMPTY LIST} only value @{EMPTY LIST}
Should Be Equal ${ret} only value
+List With Mutable Items
+ [Documentation] Resolving variables with mutable items had a bug that depended on alphabetical order.
+ ... https://github.com/robotframework/robotframework/issues/5181
+ Should Be True $AAA_MUTABLE_ITEMS[0] is $MUTABLE
+ Should Be True $AAA_MUTABLE_ITEMS[1] is $LIST
+ Should Be True $AAA_MUTABLE_ITEMS[2] is $DICT
+ Should Be True $ZZZ_MUTABLE_ITEMS[0] is $MUTABLE
+ Should Be True $ZZZ_MUTABLE_ITEMS[1] is $LIST
+ Should Be True $ZZZ_MUTABLE_ITEMS[2] is $DICT
+
Variable Names Are Case Insensitive
Should Be Equal ${lowercase} Variable name in lower case
Should Be Equal ${LOWERCASE} Variable name in lower case
diff --git a/atest/testdata/variables/variable_types.robot b/atest/testdata/variables/variable_types.robot
new file mode 100644
index 00000000000..3cb5b30b43a
--- /dev/null
+++ b/atest/testdata/variables/variable_types.robot
@@ -0,0 +1,506 @@
+*** Settings ***
+Library ../test_libraries/Embedded.py
+Variables extended_variables.py
+
+
+*** Variables ***
+${INTEGER: int} 42
+${INT_LIST: list[int]} [42, '1']
+${EMPTY_STR: str} ${EMPTY}
+@{LIST: int} 1 ${2} 3
+@{LIST_IN_LIST: list[int]} [1, 2] ${LIST}
+${NONE_TYPE: None} None
+&{DICT_1: str=int|str} a=1 b=${2} c=${None}
+&{DICT_2: int=list[int]} 1=[1, 2, 3] 2=[4, 5, 6]
+&{DICT_3: list[int]} 10=[3, 2] 20=[1, 0]
+${NO_TYPE} 42
+${BAD_VALUE: int} not int
+${BAD_TYPE: hahaa} 1
+@{BAD_LIST_VALUE: int} 1 hahaa
+@{BAD_LIST_TYPE: xxxxx} k a l a
+&{BAD_DICT_VALUE: str=int} x=a y=b
+&{BAD_DICT_TYPE: aa=bb} x=1 y=2
+&{INVALID_DICT_TYPE1: int=list[int} 1=[1, 2, 3] 2=[4, 5, 6]
+&{INVALID_DICT_TYPE2: int=listint]} 1=[1, 2, 3] 2=[4, 5, 6]
+${NAME} NO_TYPE_FROM_VAR: int
+${${NAME}} 42
+
+
+*** Test Cases ***
+Command line
+ Should Be Equal ${CLI} 2025-05-20 type=date
+ Should Be Equal ${NOT} INT:1
+ Should Be Equal ${NOT2} ${SPACE}leading space, no 2nd colon
+
+Variable section
+ Should be equal ${INTEGER} 42 type=int
+ Variable should not exist ${INTEGER: int}
+ Should be equal ${INT_LIST} [42, 1] type=list
+ Variable should not exist ${INT_LIST: list[int]}
+ Should be equal ${EMPTY_STR} ${EMPTY}
+ Variable should not exist ${EMPTY_STR: str}
+ Should be equal ${NO_TYPE} 42
+ Should be equal ${NONE_TYPE} ${None}
+ Variable should not exist ${NONE_TYPE: None}
+ Should be equal ${NO_TYPE_FROM_VAR: int} 42 type=str
+
+Variable section: List
+ Should be equal ${LIST_IN_LIST} [[1, 2], [1, 2, 3]] type=list
+ Variable should not exist ${LIST_IN_LIST: list[int]}
+ Should be equal ${LIST} ${{[1, 2, 3]}}
+ Variable should not exist ${LIST: int}
+
+Variable section: Dictionary
+ Should be equal ${DICT_1} {"a": "1", "b": 2, "c": "None"} type=dict
+ Variable should not exist ${DICT_1: str=int|str}
+ Should be equal ${DICT_2} {1: [1, 2, 3], 2: [4, 5, 6]} type=dict
+ Variable should not exist ${DICT_2: int=list[int]}
+ Should be equal ${DICT_3} {"10": [3, 2], "20": [1, 0]} type=dict
+ Variable should not exist ${DICT_3: list[int]}
+
+Variable section: With invalid values or types
+ Variable should not exist ${BAD_VALUE}
+ Variable should not exist ${BAD_VALUE: int}
+ Variable should not exist ${BAD_TYPE}
+ Variable should not exist ${BAD_TYPE: hahaa}
+ Variable should not exist ${BAD_LIST_VALUE}
+ Variable should not exist ${BAD_LIST_VALUE: int}
+ Variable should not exist ${BAD_LIST_TYPE}
+ Variable should not exist ${BAD_LIST_TYPE: xxxxx}
+ Variable should not exist ${BAD_DICT_VALUE}
+ Variable should not exist ${BAD_DICT_VALUE: str=int}
+ Variable should not exist ${BAD_DICT_TYPE}
+ Variable should not exist ${BAD_DICT_TYPE: aa=bb}
+ Variable should not exist ${INVALID_DICT_TYPE1}
+ Variable should not exist ${INVALID_DICT_TYPE1: int=list[int}
+ Variable should not exist ${INVALID_DICT_TYPE2}
+ Variable should not exist ${INVALID_DICT_TYPE2: int=listint]}
+
+VAR syntax
+ VAR ${x: int|float} 123
+ Should be equal ${x} 123 type=int
+ VAR ${x: int} 1 2 3 separator=
+ Should be equal ${x} 123 type=int
+ VAR ${name} x
+ VAR ${${name}: int} 432
+ Should be equal ${x} 432 type=int
+
+VAR syntax: List
+ VAR ${x: list} [1, "2", 3]
+ Should be equal ${x} [1, "2", 3] type=list
+ VAR @{x: int} 1 2 3
+ Should be equal ${x} [1, 2, 3] type=list
+ VAR @{x: list[int]} [1, 2] [2, 3, 4]
+ Should be equal ${x} [[1, 2], [2, 3, 4]] type=list
+
+VAR syntax: Dictionary
+ VAR &{x: int} 1=2 3=4
+ Should be equal ${x} {"1": 2, "3": 4} type=dict
+ VAR &{x: int=str} 3=4 5=6
+ Should be equal ${x} {3: "4", 5: "6"} type=dict
+ VAR &{x: int = str} 100=200 300=400
+ Should be equal ${x} {100: "200", 300: "400"} type=dict
+ VAR &{x: int=dict[str, float]} 30={"key": 1} 40={"key": 2.3}
+ Should be equal ${x} {30: {"key": 1.0}, 40: {"key": 2.3}} type=dict
+
+VAR syntax: Invalid scalar value
+ [Documentation] FAIL
+ ... Setting variable '\${x: int}' failed: Value 'KALA' cannot be converted to integer.
+ VAR ${x: int} KALA
+
+VAR syntax: Invalid scalar type
+ [Documentation] FAIL Invalid variable '\${x: hahaa}': Unrecognized type 'hahaa'.
+ VAR ${x: hahaa} KALA
+
+VAR syntax: Type can not be set as variable
+ [Documentation] FAIL Invalid variable '\${x: \${type}}': Unrecognized type '\${type}'.
+ VAR ${type} int
+ VAR ${x: ${type}} 1
+
+VAR syntax: Type syntax is not resolved from variable
+ VAR ${type} : int
+ VAR ${safari${type}} 42
+ Should be equal ${safari: int} 42 type=str
+ VAR ${type} tidii: int
+ VAR ${${type}} 4242
+ Should be equal ${tidii: int} 4242 type=str
+
+Variable assignment
+ ${x: int} = Set Variable 42
+ Should be equal ${x} 42 type=int
+
+Variable assignment: List
+ @{x: int} = Create List 1 2 3
+ Should be equal ${x} [1, 2, 3] type=list
+ @{x: list[INT]} = Create List [1, 2] [2, 3, 4]
+ Should be equal ${x} [[1, 2], [2, 3, 4]] type=list
+ ${x: list[integer]} = Create List 1 2 3
+ Should be equal ${x} [1, 2, 3] type=list
+
+Variable assignment: Dictionary
+ &{x: int} = Create Dictionary 1=2 ${3}=${4.0}
+ Should be equal ${x} {"1": 2, 3: 4} type=dict
+ &{x: int=str} = Create Dictionary 1=2 ${3}=${4.0}
+ Should be equal ${x} {1: "2", 3: "4.0"} type=dict
+ ${x: dict[str, int]} = Create dictionary 1=2 3=4
+ Should be equal ${x} {"1": 2, "3": 4} type=dict
+ &{x: int=dict[str, int]} = Create Dictionary 1={2: 3} 4={5: 6}
+ Should be equal ${x} {1: {"2": 3}, 4: {"5": 6}} type=dict
+
+Variable assignment: Invalid value
+ [Documentation] FAIL
+ ... ValueError: Return value 'kala' cannot be converted to list[int]: \
+ ... Invalid expression.
+ ${x: list[int]} = Set Variable kala
+
+Variable assignment: Invalid type
+ [Documentation] FAIL Unrecognized type 'not_a_type'.
+ ${x: list[not_a_type]} = Set Variable 1 2
+
+Variable assignment: Invalid variable type for list
+ [Documentation] FAIL
+ ... ValueError: Return value '['1', '2', '3']' (list) cannot be converted to float.
+ ${x: float} = Create List 1 2 3
+
+Variable assignment: Invalid type for list
+ [Documentation] FAIL
+ ... ValueError: Return value '['1', '2', '3']' (list) cannot be converted to list[list[int]]: \
+ ... Item '0' got value '1' that cannot be converted to list[int]: Value is integer, not list.
+ @{x: list[int]} = Create List 1 2 3
+
+Variable assignment: Invalid variable type for dictionary
+ [Documentation] FAIL Unrecognized type 'int=str'.
+ ${x: int=str} = Create dictionary 1=2 3=4
+
+Variable assignment: Multiple
+ ${a: int} ${b: float} = Create List 1 2.3
+ Should be equal ${a} 1 type=int
+ Should be equal ${b} 2.3 type=float
+
+Variable assignment: Multiple list and scalars
+ ${a: int} @{b: float} = Create List 1 2 3.4
+ Should be equal ${a} 1 type=int
+ Should be equal ${b} [2.0, 3.4] type=list
+ @{a: int} ${b: float} = Create List 1 2 3.4
+ Should be equal ${a} [1, 2] type=list
+ Should be equal ${b} 3.4 type=float
+ ${a: int} @{b: float} ${c: float} = Create List 1 2 3.4
+ Should be equal ${a} 1 type=int
+ Should be equal ${b} [2.0] type=list
+ Should be equal ${c} 3.4 type=float
+ ${a: int} @{b: float} ${c: float} ${d: float} = Create List 1 2 3.4
+ Should be equal ${a} 1 type=int
+ Should be equal ${b} [] type=list
+ Should be equal ${c} 2.0 type=float
+ Should be equal ${d} 3.4 type=float
+
+Variable assignment: Invalid type for list in multiple variable assignment
+ [Documentation] FAIL Unrecognized type 'bad'.
+ ${a: int} @{b: bad} = Create List 9 8 7
+
+Variable assignment: Type can not be set as variable
+ [Documentation] FAIL Unrecognized type '\${type}'.
+ VAR ${type} int
+ ${a: ${type}} = Set variable 123
+
+Variable assignment: Type syntax is not resolved from variable
+ VAR ${type} x: int
+ ${${type}} = Set variable 12
+ Should be equal ${x: int} 12
+
+Variable assignment: Extended
+ [Documentation] FAIL ValueError: Return value 'kala' cannot be converted to integer.
+ Should be equal ${OBJ.name} dude
+ ${OBJ.name: int} = Set variable 42
+ Should be equal ${OBJ.name} 42 type=int
+ ${OBJ.name: int} = Set variable kala
+
+Variable assignment: Item
+ [Documentation] FAIL ValueError: Return value 'kala' cannot be converted to integer.
+ VAR @{x} 1 2
+ ${x: int}[0] = Set variable 3
+ Should be equal ${x} [3, "2"] type=list
+ ${x: int}[0] = Set variable kala
+
+User keyword
+ Keyword 1 1 int
+ Keyword 1.2 1.2 float
+ Varargs 1 2 3
+ Kwargs a=1 b=2.3
+ Combination of all args 1.0 2 3 4 a=5 b=6
+
+User keyword: Default value
+ Default
+ Default 1
+ Default as string
+ Default as string ${42}
+
+User keyword: Invalid default value 1
+ [Documentation] FAIL
+ ... ValueError: Default value for argument 'arg' got value 'invalid' that cannot be converted to integer.
+ Invalid default
+
+User keyword: Invalid default value 2
+ [Documentation] FAIL
+ ... ValueError: Argument 'arg' got value 'bad' that cannot be converted to integer.
+ Invalid default 42
+ Invalid default bad
+
+User keyword: Invalid value
+ [Documentation] FAIL
+ ... ValueError: Argument 'type' got value 'bad' that cannot be \
+ ... converted to 'int', 'float' or 'third value in literal'.
+ Keyword 1.2 1.2 bad
+
+User keyword: Invalid type
+ [Documentation] FAIL
+ ... Invalid argument specification: \
+ ... Invalid argument '\${arg: bad}': \
+ ... Unrecognized type 'bad'.
+ Bad type
+
+User keyword: Invalid assignment with kwargs k_type=v_type declaration
+ [Documentation] FAIL
+ ... Invalid argument specification: \
+ ... Invalid argument '\&{kwargs: int=float}': \
+ ... Unrecognized type 'int=float'.
+ Kwargs does not support key=value type syntax
+
+Embedded arguments
+ Embedded 1 and 2
+ Embedded type 1 and no type 2
+
+Embedded arguments: With custom regexp
+ [Documentation] FAIL No keyword with name 'Embedded type with custom regular expression 1.1' found.
+ Embedded type with custom regular expression 111
+ Embedded type with custom regular expression 1.1
+
+Embedded arguments: With variables
+ VAR ${x} 1
+ VAR ${y} ${2.0}
+ Embedded ${x} and ${y}
+
+Embedded arguments: Invalid value
+ [Documentation] FAIL ValueError: Argument 'y' got value 'kala' that cannot be converted to integer.
+ Embedded 1 and kala
+
+Embedded arguments: Invalid value from variable
+ [Documentation] FAIL ValueError: Argument 'y' got value '[2, 3]' (list) that cannot be converted to integer.
+ Embedded 1 and ${{[2, 3]}}
+
+Embedded arguments: Invalid type
+ [Documentation] FAIL Invalid embedded argument '\${x: invalid}': Unrecognized type 'invalid'.
+ Embedded invalid type ${x: invalid}
+
+Variable usage does not support type syntax 1
+ [Documentation] FAIL STARTS: Resolving variable '\${x: int}' failed: SyntaxError:
+ VAR ${x} 1
+ Log This fails: ${x: int}
+
+Variable usage does not support type syntax 2
+ [Documentation] FAIL
+ ... Resolving variable '\${abc_not_here: int}' failed: \
+ ... Variable '\${abc_not_here}' not found.
+ Log ${abc_not_here: int}: fails
+
+FOR
+ VAR ${expected: int} 1
+ FOR ${item: int} IN 1 2 3
+ Should Be Equal ${item} ${expected}
+ ${expected} = Evaluate ${expected} + 1
+ END
+
+FOR: Multiple variables
+ VAR @{english} cat dog horse
+ VAR @{finnish} kissa koira hevonen
+ VAR ${index: int} 1
+ FOR ${i: int} ${en: Literal["cat", "dog", "horse"]} ${fi: str} IN
+ ... 1 cat kissa
+ ... 2 Dog koira
+ ... 3 HORSE hevonen
+ Should Be Equal ${i} ${index}
+ Should Be Equal ${en} ${english}[${index-1}]
+ Should Be Equal ${fi} ${finnish}[${index-1}]
+ ${index} = Evaluate ${index} + 1
+ END
+
+FOR: Dictionary
+ VAR &{dict} 1=2 3=4
+ VAR ${index: int} 1
+ FOR ${key: int} ${value: int} IN &{dict} 5=6
+ Should Be Equal ${key} ${index}
+ Should Be Equal ${value} ${index + 1}
+ ${index} = Evaluate ${index} + 2
+ END
+ VAR ${index: int} 1
+ FOR ${item: tuple[int, int]} IN 1=ignored &{dict} 5=6
+ Should Be Equal ${item} ${{($index, $index+1)}}
+ ${index} = Evaluate ${index} + 2
+ END
+
+FOR IN RANGE
+ VAR ${expected: int} 0
+ FOR ${x: timedelta} IN RANGE 10
+ Should Be Equal ${x.total_seconds()} ${expected}
+ ${expected} = Evaluate ${expected} + 1
+ END
+
+FOR IN ENUMERATE
+ VAR ${index: int} 0
+ FOR ${i: str} ${x: int} IN ENUMERATE 0 1 2 3 4 5
+ Should Be Equal ${i} ${index} type=str
+ Should Be Equal ${x} ${index} type=int
+ ${index} = Evaluate ${index} + 1
+ END
+ VAR ${index: int} 1
+ FOR ${item: tuple[str, int]} IN ENUMERATE 1 2 3 start=1
+ Should Be Equal ${item} ${{($index, $index)}} type=tuple[str, int]
+ ${index} = Evaluate ${index} + 1
+ END
+
+FOR IN ENUMERATE: Dictionary
+ VAR &{dict} 0=1 1=${2} ${2}=3
+ VAR ${index: int} 0
+ FOR ${i: str} ${key: int} ${value: int} IN ENUMERATE &{dict}
+ Should Be Equal ${i} ${index} type=str
+ Should Be Equal ${key} ${index}
+ Should Be Equal ${value} ${index + 1}
+ ${index} = Evaluate ${index} + 1
+ END
+ VAR ${index: int} 0
+ FOR ${i: str} ${item: tuple[int, int]} IN ENUMERATE &{dict} 3=${4.0}
+ Should Be Equal ${i} ${index} type=str
+ Should Be Equal ${item} ${{($index, $index+1)}} type=tuple[int, int]
+ ${index} = Evaluate ${index} + 1
+ END
+ VAR ${index: int} 0
+ FOR ${all: list[str]} IN ENUMERATE 0=ignore &{dict} 3=4 ${4}=${5}
+ Should Be Equal ${all} ${{[$index, $index, $index+1]}} type=list[str]
+ ${index} = Evaluate ${index} + 1
+ END
+
+FOR IN ZIP
+ VAR @{list1} ${1} ${2} ${3}
+ VAR @{list2} 1 2 3
+ VAR ${index: int} 1
+ FOR ${i1: str} ${i2: int} IN ZIP ${list1} ${list2}
+ Should Be Equal ${i1} ${index} type=str
+ Should Be Equal ${i2} ${index}
+ ${index} = Evaluate ${index} + 1
+ END
+ VAR ${index: int} 1
+ FOR ${item: tuple[str, int]} IN ZIP ${list1} ${list2}
+ Should Be Equal ${item} ${{($index, $index)}} type=tuple[str, int]
+ ${index} = Evaluate ${index} + 1
+ END
+
+FOR: Failing conversion 1
+ [Documentation] FAIL
+ ... ValueError: FOR loop variable '\${x: float}' got value 'bad' \
+ ... that cannot be converted to float.
+ FOR ${x: float} IN 1 bad 3
+ Should Be Equal ${x} 1 type=float
+ END
+
+FOR: Failing conversion 2
+ [Documentation] FAIL
+ ... ValueError: FOR loop variable '\${x: int}' got value '0.1' (float) \
+ ... that cannot be converted to integer: Conversion would lose precision.
+ FOR ${x: int} IN RANGE 0 1 0.1
+ Should Be Equal ${x} 0 type=int
+ END
+
+FOR: Failing conversion 3
+ [Documentation] FAIL
+ ... ValueError: FOR loop variable '\${i: Literal[0, 1, 2]}' got value '3' (integer) \
+ ... that cannot be converted to 0, 1 or 2.
+ VAR ${expected: int} 0
+ FOR ${i: Literal[0, 1, 2]} ${c: Literal["a", "b", "c"]} IN ENUMERATE a B c d e
+ Should Be Equal ${i} ${expected}
+ Should Be Equal ${c} ${{"abc"[$expected]}}
+ ${expected} = Evaluate ${expected} + 1
+ END
+
+FOR: Invalid type
+ [Documentation] FAIL
+ ... Invalid FOR loop variable '\${item: bad}': Unrecognized type 'bad'.
+ FOR ${item: bad} IN ENUMERATE whatever
+ Fail Not run
+ END
+
+Inline IF
+ ${x: int} = IF True Default as string ELSE Default
+ Should be equal ${x} 42 type=int
+ ${x: str} = IF False Default as string ELSE Default
+ Should be equal ${x} 1 type=str
+ ${first: int} @{rest: int | float} = IF True Create List 1 2.3 4
+ Should be equal ${first} 1 type=int
+ Should be equal ${rest} [2.3, 4] type=list
+ @{x: int} = IF False Fail Not run
+ Should be equal ${x} [] type=list
+
+Set global/suite/test/local variable: No support
+ Set local variable ${local: int} 1
+ Should be equal ${local: int} 1 type=str
+ Set test variable ${test: xxx} 2
+ Should be equal ${test: xxx} 2 type=str
+ Set suite variable ${suite: int} 3
+ Should be equal ${suite: int} 3 type=str
+ Set suite variable ${global: int} 4
+ Should be equal ${global: int} 4 type=str
+
+
+*** Keywords ***
+Keyword
+ [Arguments] ${arg: int|float} ${exp} ${type: Literal['int', 'float', 'third value in literal']}
+ Should be equal ${arg} ${exp} type=${type}
+
+Varargs
+ [Arguments] @{args: int}
+ Should be equal ${args} [1, 2, 3] type=list
+
+Kwargs
+ [Arguments] &{args: float|int}
+ Should be equal ${args} {"a":1, "b":2.3} type=dict
+
+Default
+ [Arguments] ${arg: int}=1
+ Should be equal ${arg} 1 type=int
+ RETURN ${arg}
+
+Default as string
+ [Arguments] ${arg: str}=${42}
+ Should be equal ${arg} 42 type=str
+ RETURN ${arg}
+
+Invalid default
+ [Arguments] ${arg: int}=invalid
+ Should Be Equal ${arg} 42 type=int
+
+Bad type
+ [Arguments] ${arg: bad}
+ Fail Should not be run
+
+Kwargs does not support key=value type syntax
+ [Arguments] &{kwargs: int=float}
+ Variable should not exist &{kwargs}
+
+Combination of all args
+ [Arguments] ${arg: float} @{args: int} &{kwargs: int}
+ Should be equal ${arg} 1.0 type=float
+ Should be equal ${args} [2, 3, 4] type=list[int]
+ Should be equal ${kwargs} {"a": 5, "b": 6} type=dict[str, int]
+
+Embedded ${x: int} and ${y: int}
+ Should be equal ${x} 1 type=int
+ Should be equal ${y} 2 type=int
+
+Embedded type ${x: int} and no type ${y}
+ Should be equal ${x} 1 type=int
+ Should be equal ${y} 2 type=str
+
+Embedded type with custom regular expression ${x: int:\d+}
+ Should be equal ${x} 111 type=int
+
+Embedded invalid type ${x: invalid}
+ Fail Should not be run
diff --git a/atest/testdata/variables/variables_in_import_settings/variables1.py b/atest/testdata/variables/variables_in_import_settings/variables1.py
index f5dd1857f1c..dda891ea24c 100644
--- a/atest/testdata/variables/variables_in_import_settings/variables1.py
+++ b/atest/testdata/variables/variables_in_import_settings/variables1.py
@@ -1 +1 @@
-greetings = 'Hello, world!'
\ No newline at end of file
+greetings = "Hello, world!"
diff --git a/atest/testdata/variables/variables_in_import_settings/variables2.py b/atest/testdata/variables/variables_in_import_settings/variables2.py
index a5dcc3de479..0ca60926c00 100644
--- a/atest/testdata/variables/variables_in_import_settings/variables2.py
+++ b/atest/testdata/variables/variables_in_import_settings/variables2.py
@@ -1 +1 @@
-greetings = 'Hi, Tellus!'
\ No newline at end of file
+greetings = "Hi, Tellus!"
diff --git a/atest/testresources/listeners/AddMessagesToTestBody.py b/atest/testresources/listeners/AddMessagesToTestBody.py
new file mode 100644
index 00000000000..28e0858a807
--- /dev/null
+++ b/atest/testresources/listeners/AddMessagesToTestBody.py
@@ -0,0 +1,17 @@
+from robot.api import logger
+from robot.api.deco import library
+
+
+@library(listener="SELF")
+class AddMessagesToTestBody:
+
+ def __init__(self, name=None):
+ self.name = name
+
+ def start_test(self, data, result):
+ if data.name == self.name or not self.name:
+ logger.info(f"Hello '{data.name}', says listener!")
+
+ def end_test(self, data, result):
+ if data.name == self.name or not self.name:
+ logger.info(f"Bye '{data.name}', says listener!")
diff --git a/atest/testresources/listeners/ListenAll.py b/atest/testresources/listeners/ListenAll.py
index b32c365afc7..004f5d7ce8f 100644
--- a/atest/testresources/listeners/ListenAll.py
+++ b/atest/testresources/listeners/ListenAll.py
@@ -3,100 +3,119 @@
class ListenAll:
- ROBOT_LISTENER_API_VERSION = '2'
+ ROBOT_LISTENER_API_VERSION = "2"
- def __init__(self, *path):
- path = ':'.join(path) if path else self._get_default_path()
- self.outfile = open(path, 'w')
+ def __init__(self, *path, output_file_disabled=False):
+ path = ":".join(path) if path else self._get_default_path()
+ self.outfile = open(path, "w", encoding="UTF-8")
+ self.output_file_disabled = output_file_disabled
self.start_attrs = []
def _get_default_path(self):
- return os.path.join(os.getenv('TEMPDIR'), 'listen_all.txt')
+ return os.path.join(os.getenv("TEMPDIR"), "listen_all.txt")
def start_suite(self, name, attrs):
- metastr = ' '.join('%s: %s' % (k, v) for k, v in attrs['metadata'].items())
- self.outfile.write("SUITE START: %s (%s) '%s' [%s]\n"
- % (name, attrs['id'], attrs['doc'], metastr))
+ meta = " ".join(f"{k}: {v}" for k, v in attrs["metadata"].items())
+ self.outfile.write(
+ f"SUITE START: {name} ({attrs['id']}) '{attrs['doc']}' [{meta}]\n"
+ )
self.start_attrs.append(attrs)
def start_test(self, name, attrs):
- tags = [str(tag) for tag in attrs['tags']]
- self.outfile.write("TEST START: %s (%s, line %d) '%s' %s\n"
- % (name, attrs['id'], attrs['lineno'], attrs['doc'], tags))
+ tags = [str(tag) for tag in attrs["tags"]]
+ self.outfile.write(
+ f"TEST START: {name} ({attrs['id']}, line {attrs['lineno']}) "
+ f"'{attrs['doc']}' {tags}\n"
+ )
self.start_attrs.append(attrs)
def start_keyword(self, name, attrs):
- if attrs['assign']:
- assign = '%s = ' % ', '.join(attrs['assign'])
+ if attrs["assign"]:
+ assign = ", ".join(attrs["assign"]) + " = "
else:
- assign = ''
- name = name + ' ' if name else ''
- if attrs['args']:
- args = '%s ' % [str(a) for a in attrs['args']]
+ assign = ""
+ name = name + " " if name else ""
+ if attrs["args"]:
+ args = str(attrs["args"]) + " "
else:
- args = ''
- self.outfile.write("%s START: %s%s%s(line %d)\n"
- % (attrs['type'], assign, name, args, attrs['lineno']))
+ args = ""
+ self.outfile.write(
+ f"{attrs['type']} START: {assign}{name}{args}(line {attrs['lineno']})\n"
+ )
self.start_attrs.append(attrs)
def log_message(self, message):
msg, level = self._check_message_validity(message)
- if level != 'TRACE' and 'Traceback' not in msg:
- self.outfile.write('LOG MESSAGE: [%s] %s\n' % (level, msg))
+ if level != "TRACE" and "Traceback" not in msg:
+ self.outfile.write(f"LOG MESSAGE: [{level}] {msg}\n")
def message(self, message):
msg, level = self._check_message_validity(message)
- if 'Settings' in msg:
- self.outfile.write('Got settings on level: %s\n' % level)
+ if "Settings" in msg:
+ self.outfile.write(f"Got settings on level: {level}\n")
def _check_message_validity(self, message):
- if message['html'] not in ['yes', 'no']:
- self.outfile.write('Log message has invalid `html` attribute %s' %
- message['html'])
- if not message['timestamp'].startswith(str(time.localtime()[0])):
- self.outfile.write('Log message has invalid timestamp %s' %
- message['timestamp'])
- return message['message'], message['level']
+ if message["html"] not in ["yes", "no"]:
+ self.outfile.write(
+ f"Log message has invalid `html` attribute {message['html']}."
+ )
+ if not message["timestamp"].startswith(str(time.localtime()[0])):
+ self.outfile.write(
+ f"Log message has invalid timestamp {message['timestamp']}."
+ )
+ return message["message"], message["level"]
def end_keyword(self, name, attrs):
- kw_type = 'KW' if attrs['type'] == 'Keyword' else attrs['type'].upper()
- self.outfile.write("%s END: %s\n" % (kw_type, attrs['status']))
+ kw_type = "KW" if attrs["type"] == "Keyword" else attrs["type"].upper()
+ self.outfile.write(f"{kw_type} END: {attrs['status']}\n")
self._validate_start_attrs_at_end(attrs)
def _validate_start_attrs_at_end(self, end_attrs):
start_attrs = self.start_attrs.pop()
for key in start_attrs:
- assert end_attrs[key] == start_attrs[key]
+ start = start_attrs[key]
+ end = end_attrs[key]
+ if not (end == start or (key == "status" and start == "NOT SET")):
+ raise AssertionError(
+ f"End attr {end!r} is different to " f"start attr {start!r}."
+ )
def end_test(self, name, attrs):
- if attrs['status'] == 'PASS':
- self.outfile.write('TEST END: PASS\n')
+ if attrs["status"] == "PASS":
+ self.outfile.write("TEST END: PASS\n")
else:
- self.outfile.write("TEST END: %s %s\n"
- % (attrs['status'], attrs['message']))
+ self.outfile.write(f"TEST END: {attrs['status']} {attrs['message']}\n")
self._validate_start_attrs_at_end(attrs)
def end_suite(self, name, attrs):
- self.outfile.write('SUITE END: %s %s\n'
- % (attrs['status'], attrs['statistics']))
+ self.outfile.write(f"SUITE END: {attrs['status']} {attrs['statistics']}\n")
self._validate_start_attrs_at_end(attrs)
def output_file(self, path):
- self._out_file('Output', path)
+ self._out_file("Output", path)
def report_file(self, path):
- self._out_file('Report', path)
+ self._out_file("Report", path)
def log_file(self, path):
- self._out_file('Log', path)
+ self._out_file("Log", path)
+
+ def xunit_file(self, path):
+ self._out_file("Xunit", path)
def debug_file(self, path):
- self._out_file('Debug', path)
+ self._out_file("Debug", path)
def _out_file(self, name, path):
- assert isinstance(path, str) and os.path.isabs(path)
- self.outfile.write('%s: %s\n' % (name, os.path.basename(path)))
+ if name == "Output" and self.output_file_disabled:
+ if path != "None":
+ raise AssertionError(f"Output should be disabled, got {path!r}.")
+ else:
+ if not (isinstance(path, str) and os.path.isabs(path)):
+ raise AssertionError(f"Path should be absolute, got {path!r}.")
+ path = os.path.basename(path)
+ self.outfile.write(f"{name}: {path}\n")
def close(self):
- self.outfile.write('Closing...\n')
+ self.outfile.write("Closing...\n")
self.outfile.close()
diff --git a/atest/testresources/listeners/ListenImports.py b/atest/testresources/listeners/ListenImports.py
index 4ccd7743452..7df53a4f5ec 100644
--- a/atest/testresources/listeners/ListenImports.py
+++ b/atest/testresources/listeners/ListenImports.py
@@ -5,7 +5,7 @@ class ListenImports:
ROBOT_LISTENER_API_VERSION = 2
def __init__(self, imports):
- self.imports = open(imports, 'w')
+ self.imports = open(imports, "w", encoding="UTF-8")
def library_import(self, name, attrs):
self._imported("Library", name, attrs)
@@ -17,18 +17,18 @@ def variables_import(self, name, attrs):
self._imported("Variables", name, attrs)
def _imported(self, import_type, name, attrs):
- self.imports.write("Imported %s\n\tname: %s\n" % (import_type, name))
- for name in sorted(attrs):
- self.imports.write("\t%s: %s\n" % (name, self._pretty(attrs[name])))
+ self.imports.write(f"Imported {import_type}\n\tname: {name}\n")
+ for key in sorted(attrs):
+ self.imports.write(f"\t{key}: {self._pretty(attrs[key])}\n")
def _pretty(self, entry):
if isinstance(entry, list):
- return '[%s]' % ', '.join(entry)
+ return f"[{', '.join(entry)}]"
if isinstance(entry, str) and os.path.isabs(entry):
- entry = entry.replace('$py.class', '.py').replace('.pyc', '.py')
+ entry = entry.replace(".pyc", ".py")
tokens = entry.split(os.sep)
- index = -1 if tokens[-1] != '__init__.py' else -2
- return '//' + '/'.join(tokens[index:])
+ index = -1 if tokens[-1] != "__init__.py" else -2
+ return "//" + "/".join(tokens[index:])
return entry
def close(self):
diff --git a/atest/testresources/listeners/VerifyAttributes.py b/atest/testresources/listeners/VerifyAttributes.py
index 9fc3637e2cb..81b95d6c52f 100644
--- a/atest/testresources/listeners/VerifyAttributes.py
+++ b/atest/testresources/listeners/VerifyAttributes.py
@@ -1,50 +1,57 @@
import os
-OUTFILE = open(os.path.join(os.getenv('TEMPDIR'), 'listener_attrs.txt'), 'w')
-START = 'doc starttime '
-END = START + 'endtime elapsedtime status '
-SUITE = 'id longname metadata source tests suites totaltests '
-TEST = 'id longname tags template originalname source lineno '
-KW = 'kwname libname args assign tags type lineno source status '
-KW_TYPES = {'FOR': 'variables flavor values',
- 'WHILE': 'condition limit on_limit on_limit_message',
- 'IF': 'condition',
- 'ELSE IF': 'condition',
- 'EXCEPT': 'patterns pattern_type variable',
- 'VAR': 'name value scope',
- 'RETURN': 'values'}
-FOR_FLAVOR_EXTRA = {'IN ENUMERATE': ' start',
- 'IN ZIP': ' mode fill'}
-EXPECTED_TYPES = {'tags': [str],
- 'args': [str],
- 'assign': [str],
- 'metadata': {str: str},
- 'tests': [str],
- 'suites': [str],
- 'totaltests': int,
- 'elapsedtime': int,
- 'lineno': (int, type(None)),
- 'source': (str, type(None)),
- 'variables': (dict, list),
- 'flavor': str,
- 'values': (list, dict),
- 'condition': str,
- 'limit': (str, type(None)),
- 'on_limit': (str, type(None)),
- 'on_limit_message': (str, type(None)),
- 'patterns': (str, list),
- 'pattern_type': (str, type(None)),
- 'variable': (str, type(None)),
- 'value': (str, list)}
+OUTFILE = open(
+ os.path.join(os.getenv("TEMPDIR"), "listener_attrs.txt"),
+ mode="w",
+ encoding="UTF-8",
+)
+START = "doc starttime "
+END = START + "endtime elapsedtime status "
+SUITE = "id longname metadata source tests suites totaltests "
+TEST = "id longname tags template originalname source lineno "
+KW = "kwname libname args assign tags type lineno source status "
+KW_TYPES = {
+ "FOR": "variables flavor values",
+ "WHILE": "condition limit on_limit on_limit_message",
+ "IF": "condition",
+ "ELSE IF": "condition",
+ "EXCEPT": "patterns pattern_type variable",
+ "VAR": "name value scope",
+ "RETURN": "values",
+}
+FOR_FLAVOR_EXTRA = {"IN ENUMERATE": " start", "IN ZIP": " mode fill"}
+EXPECTED_TYPES = {
+ "tags": [str],
+ "args": [str],
+ "assign": [str],
+ "metadata": {str: str},
+ "tests": [str],
+ "suites": [str],
+ "totaltests": int,
+ "elapsedtime": int,
+ "lineno": (int, type(None)),
+ "source": (str, type(None)),
+ "variables": (dict, list),
+ "flavor": str,
+ "values": (list, dict),
+ "condition": str,
+ "limit": (str, type(None)),
+ "on_limit": (str, type(None)),
+ "on_limit_message": (str, type(None)),
+ "patterns": (str, list),
+ "pattern_type": (str, type(None)),
+ "variable": (str, type(None)),
+ "value": (str, list),
+}
def verify_attrs(method_name, attrs, names):
names = set(names.split())
- OUTFILE.write(method_name + '\n')
+ OUTFILE.write(method_name + "\n")
if len(names) != len(attrs):
- OUTFILE.write(f'FAILED: wrong number of attributes\n')
- OUTFILE.write(f'Expected: {sorted(names)}\n')
- OUTFILE.write(f'Actual: {sorted(attrs)}\n')
+ OUTFILE.write("FAILED: wrong number of attributes\n")
+ OUTFILE.write(f"Expected: {sorted(names)}\n")
+ OUTFILE.write(f"Actual: {sorted(attrs)}\n")
return
for name in names:
value = attrs[name]
@@ -52,23 +59,24 @@ def verify_attrs(method_name, attrs, names):
if isinstance(exp_type, list):
verify_attr(name, value, list)
for index, item in enumerate(value):
- verify_attr('%s[%s]' % (name, index), item, exp_type[0])
+ verify_attr(f"{name}[{index}]", item, exp_type[0])
elif isinstance(exp_type, dict):
verify_attr(name, value, dict)
key_type, value_type = dict(exp_type).popitem()
for key, value in value.items():
- verify_attr('%s[%s] (key)' % (name, key), key, key_type)
- verify_attr('%s[%s] (value)' % (name, key), value, value_type)
+ verify_attr(f"{name}[{key}] (key)", key, key_type)
+ verify_attr(f"{name}[{key}] (value)", value, value_type)
else:
verify_attr(name, value, exp_type)
def verify_attr(name, value, exp_type):
if isinstance(value, exp_type):
- OUTFILE.write('passed | %s: %s\n' % (name, format_value(value)))
+ OUTFILE.write(f"passed | {name}: {format_value(value)}\n")
else:
- OUTFILE.write('FAILED | %s: %r, Expected: %s, Actual: %s\n'
- % (name, value, exp_type, type(value)))
+ OUTFILE.write(
+ f"FAILED | {name}: {value!r}, Expected: {exp_type}, Actual: {type(value)}\n"
+ )
def format_value(value):
@@ -77,66 +85,67 @@ def format_value(value):
if isinstance(value, int):
return str(value)
if isinstance(value, list):
- return '[%s]' % ', '.join(format_value(item) for item in value)
+ items = ", ".join(format_value(item) for item in value)
+ return f"[{items}]"
if isinstance(value, dict):
- return '{%s}' % ', '.join('%s: %s' % (format_value(k), format_value(v))
- for k, v in value.items())
+ items = ", ".join(f"{format_value(k)}: {format_value(value[k])}" for k in value)
+ return f"{{{items}}}"
if value is None:
- return 'None'
- return 'FAILED! Invalid argument type %s.' % type(value)
+ return "None"
+ return f"FAILED! Invalid argument type {type(value)}."
def verify_name(name, kwname=None, libname=None, **ignored):
if libname:
- if name != '%s.%s' % (libname, kwname):
- OUTFILE.write("FAILED | KW NAME: '%s' != '%s.%s'\n" % (name, libname, kwname))
+ if name != f"{libname}.{kwname}":
+ OUTFILE.write(f"FAILED | KW NAME: '{name}' != '{libname}.{kwname}'\n")
else:
if name != kwname:
- OUTFILE.write("FAILED | KW NAME: '%s' != '%s'\n" % (name, kwname))
- if libname != '':
- OUTFILE.write("FAILED | LIB NAME: '%s' != ''\n" % libname)
+ OUTFILE.write(f"FAILED | KW NAME: '{name}' != '{kwname}'\n")
+ if libname != "":
+ OUTFILE.write(f"FAILED | LIB NAME: '{libname}' != ''\n")
class VerifyAttributes:
- ROBOT_LISTENER_API_VERSION = '2'
+ ROBOT_LISTENER_API_VERSION = "2"
def __init__(self):
self._keyword_stack = []
def start_suite(self, name, attrs):
- verify_attrs('START SUITE', attrs, START + SUITE)
+ verify_attrs("START SUITE", attrs, START + SUITE)
def end_suite(self, name, attrs):
- verify_attrs('END SUITE', attrs, END + SUITE + 'statistics message')
+ verify_attrs("END SUITE", attrs, END + SUITE + "statistics message")
def start_test(self, name, attrs):
- verify_attrs('START TEST', attrs, START + TEST)
+ verify_attrs("START TEST", attrs, START + TEST)
def end_test(self, name, attrs):
- verify_attrs('END TEST', attrs, END + TEST + 'message')
+ verify_attrs("END TEST", attrs, END + TEST + "message")
def start_keyword(self, name, attrs):
- type_ = attrs['type']
- extra = KW_TYPES.get(type_, '')
- if type_ == 'ITERATION' and self._keyword_stack[-1] == 'FOR':
- extra += ' variables'
- if type_ == 'FOR':
- extra += FOR_FLAVOR_EXTRA.get(attrs['flavor'], '')
- verify_attrs('START ' + type_, attrs, START + KW + extra)
- if type_ in ('KEYWORD', 'SETUP', 'TEARDOWN'):
+ type_ = attrs["type"]
+ extra = KW_TYPES.get(type_, "")
+ if type_ == "ITERATION" and self._keyword_stack[-1] == "FOR":
+ extra += " variables"
+ if type_ == "FOR":
+ extra += FOR_FLAVOR_EXTRA.get(attrs["flavor"], "")
+ verify_attrs("START " + type_, attrs, START + KW + extra)
+ if type_ in ("KEYWORD", "SETUP", "TEARDOWN"):
verify_name(name, **attrs)
self._keyword_stack.append(type_)
def end_keyword(self, name, attrs):
self._keyword_stack.pop()
- type_ = attrs['type']
- extra = KW_TYPES.get(type_, '')
- if type_ == 'ITERATION' and self._keyword_stack[-1] == 'FOR':
- extra += ' variables'
- if type_ == 'FOR':
- extra += FOR_FLAVOR_EXTRA.get(attrs['flavor'], '')
- verify_attrs('END ' + type_, attrs, END + KW + extra)
- if type_ in ('KEYWORD', 'SETUP', 'TEARDOWN'):
+ type_ = attrs["type"]
+ extra = KW_TYPES.get(type_, "")
+ if type_ == "ITERATION" and self._keyword_stack[-1] == "FOR":
+ extra += " variables"
+ if type_ == "FOR":
+ extra += FOR_FLAVOR_EXTRA.get(attrs["flavor"], "")
+ verify_attrs("END " + type_, attrs, END + KW + extra)
+ if type_ in ("KEYWORD", "SETUP", "TEARDOWN"):
verify_name(name, **attrs)
def close(self):
diff --git a/atest/testresources/listeners/flatten_listener.py b/atest/testresources/listeners/flatten_listener.py
index b88fe38bd2d..a2e6d47e18c 100644
--- a/atest/testresources/listeners/flatten_listener.py
+++ b/atest/testresources/listeners/flatten_listener.py
@@ -1,5 +1,5 @@
class Listener:
- ROBOT_LISTENER_API_VERSION = '2'
+ ROBOT_LISTENER_API_VERSION = "2"
def __init__(self):
self.start_kw_count = 0
diff --git a/atest/testresources/listeners/listener_versions.py b/atest/testresources/listeners/listener_versions.py
index 1b0373f3ed9..6143729544e 100644
--- a/atest/testresources/listeners/listener_versions.py
+++ b/atest/testresources/listeners/listener_versions.py
@@ -1,29 +1,28 @@
import os
from pathlib import Path
-
-VERSION_FILE = Path(os.getenv('TEMPDIR'), 'listener-versions.txt')
+VERSION_FILE = Path(os.getenv("TEMPDIR"), "listener-versions.txt")
class V2:
ROBOT_LISTENER_API_VERSION = 2
def start_suite(self, name, attrs):
- assert name == attrs['longname'] == 'Pass And Fail'
- with open(VERSION_FILE, 'a') as f:
- f.write(type(self).__name__ + '\n')
+ assert name == attrs["longname"] == "Pass And Fail"
+ with open(VERSION_FILE, "a", encoding="ASCII") as f:
+ f.write(type(self).__name__ + "\n")
class V2AsNonInt(V2):
- ROBOT_LISTENER_API_VERSION = '2'
+ ROBOT_LISTENER_API_VERSION = "2"
class V3Implicit:
def start_suite(self, data, result):
- assert data.name == result.name == 'Pass And Fail'
- with open(VERSION_FILE, 'a') as f:
- f.write(type(self).__name__ + '\n')
+ assert data.name == result.name == "Pass And Fail"
+ with open(VERSION_FILE, "a", encoding="ASCII") as f:
+ f.write(type(self).__name__ + "\n")
class V3Explicit(V3Implicit):
diff --git a/atest/testresources/listeners/listeners.py b/atest/testresources/listeners/listeners.py
index 8be02086098..476fa3858e3 100644
--- a/atest/testresources/listeners/listeners.py
+++ b/atest/testresources/listeners/listeners.py
@@ -6,30 +6,30 @@
class ListenSome:
def __init__(self):
- outpath = os.path.join(os.getenv('TEMPDIR'), 'listen_some.txt')
- self.outfile = open(outpath, 'w')
+ outpath = os.path.join(os.getenv("TEMPDIR"), "listen_some.txt")
+ self.outfile = open(outpath, "w", encoding="UTF-8")
def startTest(self, data, result):
- self.outfile.write(data.name + '\n')
+ self.outfile.write(data.name + "\n")
def endSuite(self, data, result):
- self.outfile.write(result.stat_message + '\n')
+ self.outfile.write(result.stat_message + "\n")
def close(self):
self.outfile.close()
class WithArgs:
- ROBOT_LISTENER_API_VERSION = '3'
+ ROBOT_LISTENER_API_VERSION = "3"
- def __init__(self, arg1, arg2='default'):
- outpath = os.path.join(os.getenv('TEMPDIR'), 'listener_with_args.txt')
- with open(outpath, 'a') as outfile:
- outfile.write("I got arguments '%s' and '%s'\n" % (arg1, arg2))
+ def __init__(self, arg1, arg2="default"):
+ outpath = os.path.join(os.getenv("TEMPDIR"), "listener_with_args.txt")
+ with open(outpath, "a", encoding="UTF-8") as outfile:
+ outfile.write(f"I got arguments '{arg1}' and '{arg2}'\n")
class WithArgConversion:
- ROBOT_LISTENER_API_VERSION = '2'
+ ROBOT_LISTENER_API_VERSION = "2"
def __init__(self, integer: int, boolean=False):
assert integer == 42
@@ -37,100 +37,112 @@ def __init__(self, integer: int, boolean=False):
class SuiteAndTestCounts:
- ROBOT_LISTENER_API_VERSION = '2'
+ ROBOT_LISTENER_API_VERSION = "2"
exp_data = {
- "Subsuites & Custom name for 📂 'subsuites2'":
- ([], ['Subsuites', "Custom name for 📂 'subsuites2'"], 5),
- 'Subsuites':
- ([], ['Sub1', 'Sub2'], 2),
- 'Sub1':
- (['SubSuite1 First'], [], 1),
- 'Sub2':
- (['SubSuite2 First'], [], 1),
- "Custom name for 📂 'subsuites2'":
- ([], ['Sub.Suite.4', "Custom name for 📜 'subsuite3.robot'"], 3),
- "Custom name for 📜 'subsuite3.robot'":
- (['SubSuite3 First', 'SubSuite3 Second'], [], 2),
- 'Sub.Suite.4':
- (['Test From Sub Suite 4'], [], 1)
+ "Subsuites & Custom name for 📂 'subsuites2'": (
+ [],
+ ["Subsuites", "Custom name for 📂 'subsuites2'"],
+ 5,
+ ),
+ "Subsuites": ([], ["Sub1", "Sub2"], 2),
+ "Sub1": (["SubSuite1 First"], [], 1),
+ "Sub2": (["SubSuite2 First"], [], 1),
+ "Custom name for 📂 'subsuites2'": (
+ [],
+ ["Sub.Suite.4", "Custom name for 📜 'subsuite3.robot'"],
+ 3,
+ ),
+ "Custom name for 📜 'subsuite3.robot'": (
+ ["SubSuite3 First", "SubSuite3 Second"],
+ [],
+ 2,
+ ),
+ "Sub.Suite.4": (["Test From Sub Suite 4"], [], 1),
}
def start_suite(self, name, attrs):
- data = attrs['tests'], attrs['suites'], attrs['totaltests']
+ data = attrs["tests"], attrs["suites"], attrs["totaltests"]
if data != self.exp_data[name]:
- raise AssertionError('Wrong tests or suites in %s: %s != %s.'
- % (name, self.exp_data[name], data))
+ raise AssertionError(
+ f"Wrong tests or suites in {name}: {self.exp_data[name]} != {data}."
+ )
class KeywordType:
- ROBOT_LISTENER_API_VERSION = '2'
+ ROBOT_LISTENER_API_VERSION = "2"
def start_keyword(self, name, attrs):
expected = self._get_expected_type(**attrs)
- if attrs['type'] != expected:
- raise AssertionError("Wrong keyword type '%s', expected '%s'."
- % (attrs['type'], expected))
+ if attrs["type"] != expected:
+ raise AssertionError(
+ f"Wrong keyword type {attrs['type']}, expected {expected}."
+ )
def _get_expected_type(self, kwname, libname, args, source, lineno, **ignore):
- if kwname.startswith(('${x} ', '@{finnish} ')):
- return 'VAR'
- if ' IN ' in kwname:
- return 'FOR'
- if ' = ' in kwname:
- return 'ITERATION'
+ if kwname.startswith(("${x} ", "@{finnish} ")):
+ return "VAR"
+ if " IN " in kwname:
+ return "FOR"
+ if " = " in kwname:
+ return "ITERATION"
if not args:
- if "'${x}' == 'wrong'" in kwname or '${i} == 9' in kwname:
- return 'IF'
+ if "'${x}' == 'wrong'" in kwname or "${i} == 9" in kwname:
+ return "IF"
if "'${x}' == 'value'" in kwname:
- return 'ELSE IF'
- if kwname == '':
+ return "ELSE IF"
+ if kwname == "":
source = os.path.basename(source)
- if source == 'for_loops.robot':
- return 'BREAK' if lineno == 13 else 'CONTINUE'
- return 'ELSE'
- expected = args[0] if libname == 'BuiltIn' else kwname
- return {'Suite Setup': 'SETUP', 'Suite Teardown': 'TEARDOWN',
- 'Test Setup': 'SETUP', 'Test Teardown': 'TEARDOWN',
- 'Keyword Teardown': 'TEARDOWN'}.get(expected, 'KEYWORD')
+ if source == "for_loops.robot":
+ return "BREAK" if lineno == 13 else "CONTINUE"
+ return "ELSE"
+ expected = args[0] if libname == "BuiltIn" else kwname
+ return {
+ "Suite Setup": "SETUP",
+ "Suite Teardown": "TEARDOWN",
+ "Test Setup": "SETUP",
+ "Test Teardown": "TEARDOWN",
+ "Keyword Teardown": "TEARDOWN",
+ }.get(expected, "KEYWORD")
end_keyword = start_keyword
class KeywordStatus:
- ROBOT_LISTENER_API_VERSION = '2'
+ ROBOT_LISTENER_API_VERSION = "2"
def start_keyword(self, name, attrs):
- self._validate_status(attrs, 'NOT SET')
+ self._validate_status(attrs, "NOT SET")
def end_keyword(self, name, attrs):
- run_status = 'FAIL' if attrs['kwname'] == 'Fail' else 'PASS'
+ run_status = "FAIL" if attrs["kwname"] == "Fail" else "PASS"
self._validate_status(attrs, run_status)
def _validate_status(self, attrs, run_status):
- expected = 'NOT RUN' if self._not_run(attrs) else run_status
- if attrs['status'] != expected:
- raise AssertionError('Wrong keyword status %s, expected %s.'
- % (attrs['status'], expected))
+ expected = "NOT RUN" if self._not_run(attrs) else run_status
+ if attrs["status"] != expected:
+ raise AssertionError(
+ f"Wrong keyword status {attrs['status']}, expected {expected}."
+ )
def _not_run(self, attrs):
- return attrs['type'] in ('IF', 'ELSE') or attrs['args'] == ['not going here']
+ return attrs["type"] in ("IF", "ELSE") or attrs["args"] == ["not going here"]
class KeywordExecutingListener:
- ROBOT_LISTENER_API_VERSION = '2'
+ ROBOT_LISTENER_API_VERSION = "2"
def start_test(self, name, attrs):
- self._run_keyword('Start %s' % name)
+ self._run_keyword(f"Start {name}")
def end_test(self, name, attrs):
- self._run_keyword('End %s' % name)
+ self._run_keyword(f"End {name}")
def _run_keyword(self, arg):
- BuiltIn().run_keyword('Log', arg)
+ BuiltIn().run_keyword("Log", arg)
class SuiteSource:
- ROBOT_LISTENER_API_VERSION = '2'
+ ROBOT_LISTENER_API_VERSION = "2"
def __init__(self):
self._started = 0
@@ -138,35 +150,37 @@ def __init__(self):
def start_suite(self, name, attrs):
self._started += 1
- self._test_source(name, attrs['source'])
+ self._test_source(name, attrs["source"])
def end_suite(self, name, attrs):
self._ended += 1
- self._test_source(name, attrs['source'])
+ self._test_source(name, attrs["source"])
def _test_source(self, suite, source):
default = os.path.isfile
- verifier = {'Root': lambda source: source == '',
- 'Subsuites': os.path.isdir}.get(suite, default)
+ verifier = {
+ "Root": lambda source: source == "",
+ "Subsuites": os.path.isdir,
+ }.get(suite, default)
if (source and not os.path.isabs(source)) or not verifier(source):
- raise AssertionError("Suite '%s' has wrong source '%s'."
- % (suite, source))
+ raise AssertionError(f"Suite '{suite}' has wrong source '{source}'.")
def close(self):
if not (self._started == self._ended == 5):
- raise AssertionError("Wrong number of started (%d) or ended (%d) "
- "suites. Expected 5."
- % (self._started, self._ended))
+ raise AssertionError(
+ f"Wrong number of started ({self._started}) or "
+ f"ended ({self._ended}) suites. Expected 5."
+ )
class Messages:
- ROBOT_LISTENER_API_VERSION = '2'
+ ROBOT_LISTENER_API_VERSION = "2"
def __init__(self, path):
- self.output = open(path, 'w')
+ self.output = open(path, "w", encoding="UTF-8")
def log_message(self, msg):
- self.output.write('%s: %s\n' % (msg['level'], msg['message']))
+ self.output.write(f"{msg['level']}: {msg['message']}\n")
def close(self):
self.output.close()
diff --git a/atest/testresources/listeners/module_listener.py b/atest/testresources/listeners/module_listener.py
index 11bf770c175..81b7aaaddf0 100644
--- a/atest/testresources/listeners/module_listener.py
+++ b/atest/testresources/listeners/module_listener.py
@@ -1,74 +1,82 @@
import os
-outpath = os.path.join(os.getenv('TEMPDIR'), 'listen_by_module.txt')
-OUTFILE = open(outpath, 'w')
+outpath = os.path.join(os.getenv("TEMPDIR"), "listen_by_module.txt")
+OUTFILE = open(outpath, "w", encoding="UTF-8")
ROBOT_LISTENER_API_VERSION = 2
def start_suite(name, attrs):
- metastr = ' '.join('%s: %s' % (k, v) for k, v in attrs['metadata'].items())
- OUTFILE.write("SUITE START: %s (%s) '%s' [%s]\n"
- % (name, attrs['id'], attrs['doc'], metastr))
+ meta = " ".join(f"{k}: {v}" for k, v in attrs["metadata"].items())
+ OUTFILE.write(f"SUITE START: {name} ({attrs['id']}) '{attrs['doc']}' [{meta}]\n")
+
def start_test(name, attrs):
- tags = [str(tag) for tag in attrs['tags']]
- OUTFILE.write("TEST START: %s (%s, line %s) '%s' %s\n"
- % (name, attrs['id'], attrs['lineno'], attrs['doc'],
- tags))
+ tags = [str(tag) for tag in attrs["tags"]]
+ OUTFILE.write(
+ f"TEST START: {name} ({attrs['id']}, line {attrs['lineno']}) "
+ f"'{attrs['doc']}' {tags}\n"
+ )
+
def start_keyword(name, attrs):
- if attrs['assign']:
- assign = '%s = ' % ', '.join(attrs['assign'])
- else:
- assign = ''
- name = name + ' ' if name else ''
- if attrs['args']:
- args = '%s ' % [str(a) for a in attrs['args']]
- else:
- args = ''
- OUTFILE.write("%s START: %s%s%s(line %d)\n"
- % (attrs['type'], assign, name, args, attrs['lineno']))
+ call = ""
+ if attrs["assign"]:
+ call += ", ".join(attrs["assign"]) + " = "
+ if name:
+ call += name + " "
+ if attrs["args"]:
+ call += str(attrs["args"]) + " "
+ OUTFILE.write(f"{attrs['type']} START: {call}(line {attrs['lineno']})\n")
+
def log_message(message):
- msg, level = message['message'], message['level']
- if level != 'TRACE' and 'Traceback' not in msg:
- OUTFILE.write('LOG MESSAGE: [%s] %s\n' % (level, msg))
+ msg, level = message["message"], message["level"]
+ if level != "TRACE" and "Traceback" not in msg:
+ OUTFILE.write(f"LOG MESSAGE: [{level}] {msg}\n")
+
def message(message):
- msg, level = message['message'], message['level']
- if 'Settings' in msg:
- OUTFILE.write('Got settings on level: %s\n' % level)
+ if "Settings" in message["message"]:
+ OUTFILE.write(f"Got settings on level: {message['level']}\n")
+
def end_keyword(name, attrs):
- kw_type = 'KW' if attrs['type'] == 'Keyword' else attrs['type'].upper()
- OUTFILE.write("%s END: %s\n" % (kw_type, attrs['status']))
+ kw_type = "KW" if attrs["type"] == "Keyword" else attrs["type"].upper()
+ OUTFILE.write(f"{kw_type} END: {attrs['status']}\n")
+
def end_test(name, attrs):
- if attrs['status'] == 'PASS':
- OUTFILE.write('TEST END: PASS\n')
+ if attrs["status"] == "PASS":
+ OUTFILE.write("TEST END: PASS\n")
else:
- OUTFILE.write("TEST END: %s %s\n"
- % (attrs['status'], attrs['message']))
+ OUTFILE.write(f"TEST END: {attrs['status']} {attrs['message']}\n")
+
def end_suite(name, attrs):
- OUTFILE.write('SUITE END: %s %s\n' % (attrs['status'], attrs['statistics']))
+ OUTFILE.write(f"SUITE END: {attrs['status']} {attrs['statistics']}\n")
+
def output_file(path):
- _out_file('Output', path)
+ _out_file("Output", path)
+
def report_file(path):
- _out_file('Report', path)
+ _out_file("Report", path)
+
def log_file(path):
- _out_file('Log', path)
+ _out_file("Log", path)
+
def debug_file(path):
- _out_file('Debug', path)
+ _out_file("Debug", path)
+
def _out_file(name, path):
assert os.path.isabs(path)
- OUTFILE.write('%s: %s\n' % (name, os.path.basename(path)))
+ OUTFILE.write(f"{name}: {os.path.basename(path)}\n")
+
def close():
- OUTFILE.write('Closing...\n')
+ OUTFILE.write("Closing...\n")
OUTFILE.close()
diff --git a/atest/testresources/listeners/unsupported_listeners.py b/atest/testresources/listeners/unsupported_listeners.py
index 7d16836e557..35fafa08843 100644
--- a/atest/testresources/listeners/unsupported_listeners.py
+++ b/atest/testresources/listeners/unsupported_listeners.py
@@ -2,7 +2,7 @@
def close():
- sys.exit('This should not be called')
+ sys.exit("This should not be called")
class V1Listener:
@@ -13,14 +13,14 @@ def close(self):
class V4Listener:
- ROBOT_LISTENER_API_VERSION = '4'
+ ROBOT_LISTENER_API_VERSION = "4"
def close(self):
close()
class InvalidVersionListener:
- ROBOT_LISTENER_API_VERSION = 'kekkonen'
+ ROBOT_LISTENER_API_VERSION = "kekkonen"
def close(self):
close()
diff --git a/atest/testresources/res_and_var_files/different_variables.py b/atest/testresources/res_and_var_files/different_variables.py
index 7c270d83326..0fe34711796 100644
--- a/atest/testresources/res_and_var_files/different_variables.py
+++ b/atest/testresources/res_and_var_files/different_variables.py
@@ -1,3 +1,3 @@
-list1 = [1, 2, 3, 4, 'foo', 'bar']
-dictionary1 = {'a': 1}
-dictionary2 = {'a': 1, 'b': 2}
+list1 = [1, 2, 3, 4, "foo", "bar"]
+dictionary1 = {"a": 1}
+dictionary2 = {"a": 1, "b": 2}
diff --git a/atest/testresources/res_and_var_files/resvar_subdir/variables_in_pythonpath_2.py b/atest/testresources/res_and_var_files/resvar_subdir/variables_in_pythonpath_2.py
index 7751a3af6ed..3611dc5fabd 100644
--- a/atest/testresources/res_and_var_files/resvar_subdir/variables_in_pythonpath_2.py
+++ b/atest/testresources/res_and_var_files/resvar_subdir/variables_in_pythonpath_2.py
@@ -1,3 +1,2 @@
def get_variables(*args):
- return { 'PPATH_VARFILE_2' : ' '.join(args),
- 'LIST__PPATH_VARFILE_2_LIST' : args }
+ return {"PPATH_VARFILE_2": " ".join(args), "LIST__PPATH_VARFILE_2_LIST": args}
diff --git a/atest/testresources/res_and_var_files/variables_in_pythonpath.py b/atest/testresources/res_and_var_files/variables_in_pythonpath.py
index cfdd1269812..2d3c9abec39 100644
--- a/atest/testresources/res_and_var_files/variables_in_pythonpath.py
+++ b/atest/testresources/res_and_var_files/variables_in_pythonpath.py
@@ -1 +1 @@
-PPATH_VARFILE = "Variable from variable file in PYTHONPATH"
\ No newline at end of file
+PPATH_VARFILE = "Variable from variable file in PYTHONPATH"
diff --git a/atest/testresources/testlibs/ArgumentsPython.py b/atest/testresources/testlibs/ArgumentsPython.py
index d413d6e78f3..58f45d6a1e2 100644
--- a/atest/testresources/testlibs/ArgumentsPython.py
+++ b/atest/testresources/testlibs/ArgumentsPython.py
@@ -4,32 +4,32 @@ class ArgumentsPython:
def a_0(self):
"""(0,0)"""
- return 'a_0'
+ return "a_0"
def a_1(self, arg):
"""(1,1)"""
- return 'a_1: ' + arg
+ return "a_1: " + arg
def a_3(self, arg1, arg2, arg3):
"""(3,3)"""
- return ' '.join(['a_3:',arg1,arg2,arg3])
+ return " ".join(["a_3:", arg1, arg2, arg3])
- def a_0_1(self, arg='default'):
+ def a_0_1(self, arg="default"):
"""(0,1)"""
- return 'a_0_1: ' + arg
+ return "a_0_1: " + arg
- def a_1_3(self, arg1, arg2='default', arg3='default'):
+ def a_1_3(self, arg1, arg2="default", arg3="default"):
"""(1,3)"""
- return ' '.join(['a_1_3:',arg1,arg2,arg3])
+ return " ".join(["a_1_3:", arg1, arg2, arg3])
def a_0_n(self, *args):
"""(0,sys.maxsize)"""
- return ' '.join(['a_0_n:', ' '.join(args)])
+ return " ".join(["a_0_n:", " ".join(args)])
def a_1_n(self, arg, *args):
"""(1,sys.maxsize)"""
- return ' '.join(['a_1_n:', arg, ' '.join(args)])
+ return " ".join(["a_1_n:", arg, " ".join(args)])
- def a_1_2_n(self, arg1, arg2='default', *args):
+ def a_1_2_n(self, arg1, arg2="default", *args):
"""(1,sys.maxsize)"""
- return ' '.join(['a_1_2_n:', arg1, arg2, ' '.join(args)])
+ return " ".join(["a_1_2_n:", arg1, arg2, " ".join(args)])
diff --git a/atest/testresources/testlibs/BinaryDataLibrary.py b/atest/testresources/testlibs/BinaryDataLibrary.py
index 2f90f0aad7d..d0076dbcfb4 100644
--- a/atest/testresources/testlibs/BinaryDataLibrary.py
+++ b/atest/testresources/testlibs/BinaryDataLibrary.py
@@ -6,12 +6,14 @@ class BinaryDataLibrary:
def print_bytes(self):
"""Prints all bytes in range 0-255. Many of them are control chars."""
for i in range(256):
- print("*INFO* Byte %d: '%s'" % (i, chr(i)))
+ print(f"*INFO* Byte {i}: '{chr(i)}'")
print("*INFO* All bytes printed successfully")
def raise_byte_error(self):
- raise AssertionError("Bytes 0, 10, 127, 255: '%s', '%s', '%s', '%s'"
- % (chr(0), chr(10), chr(127), chr(255)))
+ raise AssertionError(
+ f"Bytes 0, 10, 127, 255: "
+ f"'{chr(0)}', '{chr(10)}', '{chr(127)}', '{chr(255)}'"
+ )
def print_binary_data(self):
print(os.urandom(100))
diff --git a/atest/testresources/testlibs/ExampleLibrary.py b/atest/testresources/testlibs/ExampleLibrary.py
index 4e87b21f863..12e80a9da76 100644
--- a/atest/testresources/testlibs/ExampleLibrary.py
+++ b/atest/testresources/testlibs/ExampleLibrary.py
@@ -3,14 +3,14 @@
import time
import traceback
-from robot.utils import eq, normalize, timestr_to_secs
-
from objecttoreturn import ObjectToReturn
+from robot.utils import eq, normalize, timestr_to_secs
+
class ExampleLibrary:
- def print_(self, msg, stream='stdout'):
+ def print_(self, msg, stream="stdout"):
"""Print given message to selected stream (stdout or stderr)"""
print(msg, file=getattr(sys, stream))
@@ -23,12 +23,12 @@ def print_n_times(self, msg, count, delay=0):
def print_many(self, *msgs):
"""Print given messages"""
for msg in msgs:
- print(msg, end=' ')
+ print(msg, end=" ")
print()
def print_to_stdout_and_stderr(self, msg):
- print('stdout: ' + msg, file=sys.stdout)
- print('stderr: ' + msg, file=sys.stderr)
+ print("stdout: " + msg, file=sys.stdout)
+ print("stderr: " + msg, file=sys.stderr)
def single_line_doc(self):
"""One line keyword documentation."""
@@ -49,14 +49,14 @@ def exception(self, name, msg="", class_only=False):
raise exception(msg)
def external_exception(self, name, msg):
- ObjectToReturn('failure').exception(name, msg)
+ ObjectToReturn("failure").exception(name, msg)
def implicitly_chained_exception(self):
try:
try:
- 1/0
+ 1 / 0
except Exception:
- ooops
+ ooops # noqa: F821
except Exception:
self._log_python_traceback()
raise
@@ -66,28 +66,28 @@ def explicitly_chained_exception(self):
try:
assert False
except Exception as err:
- raise AssertionError('Expected error') from err
+ raise AssertionError("Expected error") from err
except Exception:
self._log_python_traceback()
raise
def _log_python_traceback(self):
- print(''.join(traceback.format_exception(*sys.exc_info())).rstrip())
+ print("".join(traceback.format_exception(*sys.exc_info())).rstrip())
- def return_string_from_library(self,string='This is a string from Library'):
+ def return_string_from_library(self, string="This is a string from Library"):
return string
def return_list_from_library(self, *args):
return list(args)
- def return_three_strings_from_library(self, one='one', two='two', three='three'):
+ def return_three_strings_from_library(self, one="one", two="two", three="three"):
return one, two, three
- def return_object(self, name=''):
+ def return_object(self, name=""):
return ObjectToReturn(name)
def check_object_name(self, object, name):
- assert object.name == name, '%s != %s' % (object.name, name)
+ assert object.name == name, f"{object.name} != {name}"
def set_object_name(self, object, name):
object.name = name
@@ -102,32 +102,38 @@ def check_attribute(self, name, expected):
try:
actual = getattr(self, normalize(name))
except AttributeError:
- raise AssertionError("Attribute '%s' not set" % name)
+ raise AssertionError(f"Attribute '{name}' not set.")
if not eq(actual, expected):
- raise AssertionError("Attribute '%s' was '%s', expected '%s'"
- % (name, actual, expected))
+ raise AssertionError(
+ f"Attribute '{name}' was '{actual}', expected '{expected}'."
+ )
def check_attribute_not_set(self, name):
if hasattr(self, normalize(name)):
- raise AssertionError("Attribute '%s' should not be set" % name)
+ raise AssertionError(f"Attribute '{name}' should not be set.")
def backslashes(self, count=1):
- return '\\' * int(count)
+ return "\\" * int(count)
def read_and_log_file(self, path, binary=False):
- mode = binary and 'rb' or 'r'
- _file = open(path, mode)
+ if binary:
+ mode = "rb"
+ encoding = None
+ else:
+ mode = "r"
+ encoding = "UTF-8"
+ _file = open(path, mode, encoding=encoding)
print(_file.read())
_file.close()
def print_control_chars(self):
- print('\033[31mRED\033[m\033[32mGREEN\033[m')
+ print("\033[31mRED\033[m\033[32mGREEN\033[m")
- def long_message(self, line_length, line_count, chars='a'):
+ def long_message(self, line_length, line_count, chars="a"):
line_length = int(line_length)
line_count = int(line_count)
- msg = chars*line_length + '\n'
- print(msg*line_count)
+ msg = chars * line_length + "\n"
+ print(msg * line_count)
def loop_forever(self, no_print=False):
i = 0
@@ -135,12 +141,12 @@ def loop_forever(self, no_print=False):
i += 1
self._sleep(1)
if not no_print:
- print('Looping forever: %d' % i)
+ print(f"Looping forever: {i}")
def write_to_file_after_sleeping(self, path, sec, msg=None):
- with open(path, 'w') as file:
+ with open(path, "w", encoding="UTF-8") as file:
self._sleep(sec)
- file.write(msg or 'Slept %s seconds' % sec)
+ file.write(msg or f"Slept {sec} seconds")
def sleep_without_logging(self, timestr):
seconds = timestr_to_secs(timestr)
@@ -174,12 +180,22 @@ def __str__(self):
return FailingStr(), FailingStr()
def fail_with_suppressed_exception_name(self, msg):
- raise MyException(msg)
+ raise ExceptionWithSuppressedName(msg)
+
+ def exception_with_empty_message_and_name(self):
+ raise ExceptionWithEmptyName("")
class _MyList(list):
pass
-class MyException(AssertionError):
+class ExceptionWithSuppressedName(AssertionError):
ROBOT_SUPPRESS_NAME = True
+
+
+class ExceptionWithEmptyName(AssertionError):
+ pass
+
+
+ExceptionWithEmptyName.__name__ = ""
diff --git a/atest/testresources/testlibs/Exceptions.py b/atest/testresources/testlibs/Exceptions.py
index 9240b65ee68..6ac8e13153f 100644
--- a/atest/testresources/testlibs/Exceptions.py
+++ b/atest/testresources/testlibs/Exceptions.py
@@ -9,12 +9,12 @@ class ContinuableApocalypseException(RuntimeError):
ROBOT_CONTINUE_ON_FAILURE = True
-def exit_on_failure(msg='BANG!', standard=False, **config):
+def exit_on_failure(msg="BANG!", standard=False, **config):
exception = FatalError if standard else FatalCatastrophyException
raise exception(msg, **config)
-def raise_continuable_failure(msg='Can be continued', standard=False):
+def raise_continuable_failure(msg="Can be continued", standard=False):
exception = ContinuableFailure if standard else ContinuableApocalypseException
raise exception(msg)
diff --git a/atest/testresources/testlibs/ExtendPythonLib.py b/atest/testresources/testlibs/ExtendPythonLib.py
index fb82eb14d70..fddc40da964 100644
--- a/atest/testresources/testlibs/ExtendPythonLib.py
+++ b/atest/testresources/testlibs/ExtendPythonLib.py
@@ -4,10 +4,10 @@
class ExtendPythonLib(ExampleLibrary):
def kw_in_python_extender(self, arg):
- return arg/2
+ return arg / 2
def print_many(self, *msgs):
- raise Exception('Overridden kw executed!')
+ raise Exception("Overridden kw executed!")
def using_method_from_python_parent(self):
- self.exception('AssertionError', 'Error message from lib')
+ self.exception("AssertionError", "Error message from lib")
diff --git a/atest/testresources/testlibs/GetKeywordNamesLibrary.py b/atest/testresources/testlibs/GetKeywordNamesLibrary.py
index 80dabdbf4d6..5d1c4c99efe 100644
--- a/atest/testresources/testlibs/GetKeywordNamesLibrary.py
+++ b/atest/testresources/testlibs/GetKeywordNamesLibrary.py
@@ -3,39 +3,46 @@
def passing_handler(*args):
for arg in args:
- print(arg, end=' ')
- return ', '.join(args)
+ print(arg, end=" ")
+ return ", ".join(args)
def failing_handler(*args):
- raise AssertionError('Failure: %s' % ' '.join(args) if args else 'Failure')
+ raise AssertionError(f"Failure: {' '.join(args)}" if args else "Failure")
class GetKeywordNamesLibrary:
def __init__(self):
- self.not_method_or_function = 'This is just a string!!'
+ self.not_method_or_function = "This is just a string!!"
def get_keyword_names(self):
- marked = [name for name in dir(self)
- if hasattr(getattr(self, name), 'robot_name')]
- other = ['Get Keyword That Passes', 'Get Keyword That Fails',
- 'keyword_in_library_itself', '_starting_with_underscore_is_ok',
- 'Non-existing attribute', 'not_method_or_function',
- 'Unexpected error getting attribute', '__init__']
+ marked = [
+ name for name in dir(self) if hasattr(getattr(self, name), "robot_name")
+ ]
+ other = [
+ "Get Keyword That Passes",
+ "Get Keyword That Fails",
+ "keyword_in_library_itself",
+ "_starting_with_underscore_is_ok",
+ "Non-existing attribute",
+ "not_method_or_function",
+ "Unexpected error getting attribute",
+ "__init__",
+ ]
return marked + other
def __getattr__(self, name):
- if name == 'Get Keyword That Passes':
+ if name == "Get Keyword That Passes":
return passing_handler
- if name == 'Get Keyword That Fails':
+ if name == "Get Keyword That Fails":
return failing_handler
- if name == 'Unexpected error getting attribute':
- raise TypeError('Oooops!')
- raise AttributeError("Non-existing attribute '%s'" % name)
+ if name == "Unexpected error getting attribute":
+ raise TypeError("Oooops!")
+ raise AttributeError(f"Non-existing attribute '{name}'")
def keyword_in_library_itself(self):
- msg = 'No need for __getattr__ here!!'
+ msg = "No need for __getattr__ here!!"
print(msg)
return msg
@@ -50,6 +57,6 @@ def name_set_in_method_signature(self):
def keyword_name_should_not_change(self):
pass
- @keyword('Add ${count} copies of ${item} to cart')
+ @keyword("Add ${count} copies of ${item} to cart")
def add_copies_to_cart(self, count, item):
return count, item
diff --git a/atest/testresources/testlibs/LenLibrary.py b/atest/testresources/testlibs/LenLibrary.py
index a1bab5c56f2..710643719ca 100644
--- a/atest/testresources/testlibs/LenLibrary.py
+++ b/atest/testresources/testlibs/LenLibrary.py
@@ -8,6 +8,7 @@ class LenLibrary:
>>> l.set_length(1)
>>> assert l
"""
+
def __init__(self):
self._length = 0
diff --git a/atest/testresources/testlibs/NamespaceUsingLibrary.py b/atest/testresources/testlibs/NamespaceUsingLibrary.py
index cbcbccae577..39bee2c89b9 100644
--- a/atest/testresources/testlibs/NamespaceUsingLibrary.py
+++ b/atest/testresources/testlibs/NamespaceUsingLibrary.py
@@ -1,10 +1,11 @@
from robot.libraries.BuiltIn import BuiltIn
+
class NamespaceUsingLibrary:
def __init__(self):
- self._importing_suite = BuiltIn().get_variable_value('${SUITE NAME}')
- self._easter = BuiltIn().get_library_instance('Easter')
+ self._importing_suite = BuiltIn().get_variable_value("${SUITE NAME}")
+ self._easter = BuiltIn().get_library_instance("Easter")
def get_importing_suite(self):
return self._importing_suite
diff --git a/atest/testresources/testlibs/NonAsciiLibrary.py b/atest/testresources/testlibs/NonAsciiLibrary.py
index 0769303d0dd..183d8503aaf 100644
--- a/atest/testresources/testlibs/NonAsciiLibrary.py
+++ b/atest/testresources/testlibs/NonAsciiLibrary.py
@@ -1,6 +1,8 @@
-MESSAGES = ['Circle is 360°',
- 'Hyvää üötä',
- '\u0989\u09C4 \u09F0 \u09FA \u099F \u09EB \u09EA \u09B9']
+MESSAGES = [
+ "Circle is 360°",
+ "Hyvää üötä",
+ "\u0989\u09c4 \u09f0 \u09fa \u099f \u09eb \u09ea \u09b9",
+]
class NonAsciiLibrary:
@@ -8,7 +10,7 @@ class NonAsciiLibrary:
def print_non_ascii_strings(self):
"""Prints message containing non-ASCII characters"""
for msg in MESSAGES:
- print('*INFO*' + msg)
+ print("*INFO*" + msg)
def print_and_return_non_ascii_object(self):
"""Prints object with non-ASCII `str()` and returns it."""
@@ -17,13 +19,13 @@ def print_and_return_non_ascii_object(self):
return obj
def raise_non_ascii_error(self):
- raise AssertionError(', '.join(MESSAGES))
+ raise AssertionError(", ".join(MESSAGES))
class NonAsciiObject:
def __init__(self):
- self.message = ', '.join(MESSAGES)
+ self.message = ", ".join(MESSAGES)
def __str__(self):
return self.message
diff --git a/atest/testresources/testlibs/ParameterLibrary.py b/atest/testresources/testlibs/ParameterLibrary.py
index f3d314fb4ee..3632e7cf287 100644
--- a/atest/testresources/testlibs/ParameterLibrary.py
+++ b/atest/testresources/testlibs/ParameterLibrary.py
@@ -3,22 +3,38 @@
class ParameterLibrary:
- def __init__(self, host='localhost', port='8080'):
+ def __init__(self, host="localhost", port="8080"):
self.host = host
self.port = port
def parameters(self):
return self.host, self.port
- def parameters_should_be(self, host='localhost', port='8080'):
+ def parameters_should_be(self, host="localhost", port="8080"):
should_be_equal = BuiltIn().should_be_equal
should_be_equal(self.host, host)
should_be_equal(self.port, port)
-class V1(ParameterLibrary): pass
-class V2(ParameterLibrary): pass
-class V3(ParameterLibrary): pass
-class V4(ParameterLibrary): pass
-class V5(ParameterLibrary): pass
-class V6(ParameterLibrary): pass
+class V1(ParameterLibrary):
+ pass
+
+
+class V2(ParameterLibrary):
+ pass
+
+
+class V3(ParameterLibrary):
+ pass
+
+
+class V4(ParameterLibrary):
+ pass
+
+
+class V5(ParameterLibrary):
+ pass
+
+
+class V6(ParameterLibrary):
+ pass
diff --git a/atest/testresources/testlibs/PythonVarArgsConstructor.py b/atest/testresources/testlibs/PythonVarArgsConstructor.py
index a33ed91dece..deedb486855 100644
--- a/atest/testresources/testlibs/PythonVarArgsConstructor.py
+++ b/atest/testresources/testlibs/PythonVarArgsConstructor.py
@@ -1,9 +1,8 @@
class PythonVarArgsConstructor:
-
+
def __init__(self, mandatory, *varargs):
self.mandatory = mandatory
self.varargs = varargs
def get_args(self):
- return self.mandatory, ' '.join(self.varargs)
-
+ return self.mandatory, " ".join(self.varargs)
diff --git a/atest/testresources/testlibs/RunKeywordLibrary.py b/atest/testresources/testlibs/RunKeywordLibrary.py
index cf91e79d9d7..ac6a0f75928 100644
--- a/atest/testresources/testlibs/RunKeywordLibrary.py
+++ b/atest/testresources/testlibs/RunKeywordLibrary.py
@@ -1,8 +1,8 @@
class RunKeywordLibrary:
- ROBOT_LIBRARY_SCOPE = 'TESTCASE'
+ ROBOT_LIBRARY_SCOPE = "TESTCASE"
def __init__(self):
- self.kw_names = ['Run Keyword That Passes', 'Run Keyword That Fails']
+ self.kw_names = ["Run Keyword That Passes", "Run Keyword That Fails"]
def get_keyword_names(self):
return self.kw_names
@@ -16,23 +16,21 @@ def run_keyword(self, name, args):
def _passes(self, args):
for arg in args:
- print(arg, end=' ')
- return ', '.join(args)
+ print(arg, end=" ")
+ return ", ".join(args)
def _fails(self, args):
- if not args:
- raise AssertionError('Failure')
- raise AssertionError('Failure: %s' % ' '.join(args))
+ raise AssertionError(f"Failure: {' '.join(args)}" if args else "Failure")
class GlobalRunKeywordLibrary(RunKeywordLibrary):
- ROBOT_LIBRARY_SCOPE = 'GLOBAL'
+ ROBOT_LIBRARY_SCOPE = "GLOBAL"
class RunKeywordButNoGetKeywordNamesLibrary:
def run_keyword(self, *args):
- return ' '.join(args)
+ return " ".join(args)
def some_other_keyword(self, *args):
- return ' '.join(args)
+ return " ".join(args)
diff --git a/atest/testresources/testlibs/SameNamesAsInBuiltIn.py b/atest/testresources/testlibs/SameNamesAsInBuiltIn.py
index 1409bc5b966..1287b5cbe07 100644
--- a/atest/testresources/testlibs/SameNamesAsInBuiltIn.py
+++ b/atest/testresources/testlibs/SameNamesAsInBuiltIn.py
@@ -1,4 +1,4 @@
class SameNamesAsInBuiltIn:
-
+
def noop(self):
- """Using this keyword without libname causes an error"""
\ No newline at end of file
+ """Using this keyword without libname causes an error"""
diff --git a/atest/testresources/testlibs/classes.py b/atest/testresources/testlibs/classes.py
index c2aabee0913..079e6ebe1bc 100644
--- a/atest/testresources/testlibs/classes.py
+++ b/atest/testresources/testlibs/classes.py
@@ -1,13 +1,12 @@
-import os.path
import functools
+import os.path
from robot.api.deco import library
+__version__ = "N/A" # This should be ignored when version is parsed
-__version__ = 'N/A' # This should be ignored when version is parsed
-
-class NameLibrary: # Old-style class on purpose!
+class NameLibrary: # Old-style class on purpose!
handler_count = 10
def simple1(self):
@@ -52,14 +51,14 @@ class DocLibrary:
def no_doc(self):
pass
- no_doc.expected_doc = ''
- no_doc.expected_shortdoc = ''
+ no_doc.expected_doc = ""
+ no_doc.expected_shortdoc = ""
def one_line_doc(self):
"""One line doc"""
- one_line_doc.expected_doc = 'One line doc'
- one_line_doc.expected_shortdoc = 'One line doc'
+ one_line_doc.expected_doc = "One line doc"
+ one_line_doc.expected_shortdoc = "One line doc"
def multiline_doc(self):
"""First line is short doc.
@@ -68,8 +67,10 @@ def multiline_doc(self):
multiple lines.
"""
- multiline_doc.expected_doc = 'First line is short doc.\n\nFull doc spans\nmultiple lines.'
- multiline_doc.expected_shortdoc = 'First line is short doc.'
+ multiline_doc.expected_doc = (
+ "First line is short doc.\n\nFull doc spans\nmultiple lines."
+ )
+ multiline_doc.expected_shortdoc = "First line is short doc."
def multiline_doc_with_split_short_doc(self):
"""Short doc can be split into
@@ -83,7 +84,7 @@ def multiline_doc_with_split_short_doc(self):
Still body.
"""
- multiline_doc_with_split_short_doc.expected_doc = '''\
+ multiline_doc_with_split_short_doc.expected_doc = """\
Short doc can be split into
multiple
physical
@@ -92,12 +93,12 @@ def multiline_doc_with_split_short_doc(self):
This is documentation body and not included
in short doc.
-Still body.'''
- multiline_doc_with_split_short_doc.expected_shortdoc = '''\
+Still body."""
+ multiline_doc_with_split_short_doc.expected_shortdoc = """\
Short doc can be split into
multiple
physical
-lines.'''
+lines."""
class ArgInfoLibrary:
@@ -107,7 +108,8 @@ def no_args(self):
"""(), {}, None, None"""
# Argument inspection had a bug when there was args on function body
# so better keep some of them around here.
- a=b=c=1
+ a = b = c = 1
+ print(a, b, c)
def required1(self, one):
"""('one',), {}, None, None"""
@@ -122,19 +124,19 @@ def required9(self, one, two, three, four, five, six, seven, eight, nine):
def default1(self, one=1):
"""('one',), {'one': 1}, None, None"""
- def default5(self, one='', two=None, three=3, four='huh', five=True):
+ def default5(self, one="", two=None, three=3, four="huh", five=True):
"""('one', 'two', 'three', 'four', 'five'), \
{'one': '', 'two': None, 'three': 3, 'four': 'huh', 'five': True}, \
None, None"""
- def required1_default1(self, one, two=''):
+ def required1_default1(self, one, two=""):
"""('one', 'two'), {'two': ''}, None, None"""
def required2_default3(self, one, two, three=3, four=4, five=5):
"""('one', 'two', 'three', 'four', 'five'), \
{'three': 3, 'four': 4, 'five': 5}, None, None"""
- def varargs(self,*one):
+ def varargs(self, *one):
"""(), {}, 'one', None"""
def required2_varargs(self, one, two, *three):
@@ -144,7 +146,9 @@ def req4_def2_varargs(self, one, two, three, four, five=5, six=6, *seven):
"""('one', 'two', 'three', 'four', 'five', 'six'), \
{'five': 5, 'six': 6}, 'seven', None"""
- def req2_def3_varargs_kwargs(self, three, four, five=5, six=6, seven=7, *eight, **nine):
+ def req2_def3_varargs_kwargs(
+ self, three, four, five=5, six=6, seven=7, *eight, **nine
+ ):
"""('three', 'four', 'five', 'six', 'seven'), \
{'five': 5, 'six': 6, 'seven': 7}, 'eight', 'nine'"""
@@ -154,7 +158,7 @@ def varargs_kwargs(self, *one, **two):
class GetattrLibrary:
handler_count = 3
- keyword_names = ['foo','bar','zap']
+ keyword_names = ["foo", "bar", "zap"]
def get_keyword_names(self):
return self.keyword_names
@@ -162,6 +166,7 @@ def get_keyword_names(self):
def __getattr__(self, name):
def handler(*args):
return name, args
+
if name not in self.keyword_names:
raise AttributeError
return handler
@@ -179,9 +184,9 @@ def handler(self):
@library(auto_keywords=True)
class VersionLibrary:
- ROBOT_LIBRARY_VERSION = '0.1'
- ROBOT_LIBRARY_DOC_FORMAT = 'html'
- kw = lambda x:None
+ ROBOT_LIBRARY_VERSION = "0.1"
+ ROBOT_LIBRARY_DOC_FORMAT = "html"
+ kw = lambda x: None
class VersionObjectLibrary:
@@ -189,15 +194,16 @@ class VersionObjectLibrary:
class _Version:
def __init__(self, ver):
self._ver = ver
+
def __str__(self):
return self._ver
- ROBOT_LIBRARY_VERSION = _Version('ver')
- kw = lambda x:None
+ ROBOT_LIBRARY_VERSION = _Version("ver")
+ kw = lambda x: None
class RecordingLibrary:
- ROBOT_LIBRARY_SCOPE = 'GLOBAL'
+ ROBOT_LIBRARY_SCOPE = "GLOBAL"
def __init__(self):
self.kw_accessed = 0
@@ -207,7 +213,7 @@ def kw(self):
self.kw_called += 1
def __getattribute__(self, name):
- if name == 'kw':
+ if name == "kw":
self.kw_accessed += 1
return object.__getattribute__(self, name)
@@ -215,24 +221,27 @@ def __getattribute__(self, name):
class ArgDocDynamicLibrary:
def __init__(self):
- kws = [('No Arg', [], None),
- ('One Arg', ['arg'], None),
- ('One or Two Args', ['arg', 'darg=dvalue'], None),
- ('Default as tuple', [('arg',), ('d1', False), ('d2', None)], None),
- ('Many Args', ['*args'], None),
- ('No Arg Spec', None, None),
- ('Multiline', None, 'Multiline\nshort doc!\n\nBody\nhere.')]
- self._keywords = dict((name, _KeywordInfo(name, argspec, doc))
- for name, argspec, doc in kws)
+ kws = [
+ ("No Arg", [], None),
+ ("One Arg", ["arg"], None),
+ ("One or Two Args", ["arg", "darg=dvalue"], None),
+ ("Default as tuple", [("arg",), ("d1", False), ("d2", None)], None),
+ ("Many Args", ["*args"], None),
+ ("No Arg Spec", None, None),
+ ("Multiline", None, "Multiline\nshort doc!\n\nBody\nhere."),
+ ]
+ self._keywords = {
+ name: _KeywordInfo(name, argspec, doc) for name, argspec, doc in kws
+ }
def get_keyword_names(self):
return sorted(self._keywords)
def run_keyword(self, name, args):
- print('*INFO* Executed keyword "%s" with arguments %s.' % (name, args))
+ print(f'*INFO* Executed keyword "{name}" with arguments {args}.')
def get_keyword_documentation(self, name):
- if name in ('__init__', '__intro__'):
+ if name in ("__init__", "__intro__"):
raise ValueError(f"'{name}' should be used only with Libdoc'")
try:
return self._keywords[name].doc
@@ -247,28 +256,33 @@ class ArgDocDynamicLibraryWithKwargsSupport(ArgDocDynamicLibrary):
def __init__(self):
ArgDocDynamicLibrary.__init__(self)
- for name, argspec in [('Kwargs', ['**kwargs']),
- ('Varargs and Kwargs', ['*args', '**kwargs'])]:
+ for name, argspec in [
+ ("Kwargs", ["**kwargs"]),
+ ("Varargs and Kwargs", ["*args", "**kwargs"]),
+ ]:
self._keywords[name] = _KeywordInfo(name, argspec)
def run_keyword(self, name, args, kwargs={}):
- argstr = ' '.join([str(a) for a in args] +
- ['%s:%s' % kv for kv in sorted(kwargs.items())])
- print('*INFO* Executed keyword %s with arguments %s' % (name, argstr))
+ argstr = " ".join(
+ [str(a) for a in args] + [f"{k}:{kwargs[k]}" for k in sorted(kwargs)]
+ )
+ print(f"*INFO* Executed keyword {name} with arguments {argstr}")
class DynamicWithSource:
- path = os.path.normpath(os.path.dirname(__file__) + '/classes.py')
- keywords = {'only path': path,
- 'path & lineno': path + ':42',
- 'lineno only': ':6475',
- 'invalid path': 'path validity is not validated',
- 'path w/ colon': r'c:\temp\lib.py',
- 'path w/ colon & lineno': r'c:\temp\lib.py:1234567890',
- 'no source': None,
- 'nön-äscii': 'hyvä esimerkki',
- 'nön-äscii utf-8': b'\xe7\xa6\x8f:88',
- 'invalid source': 666}
+ path = os.path.normpath(os.path.dirname(__file__) + "/classes.py")
+ keywords = {
+ "only path": path,
+ "path & lineno": path + ":42",
+ "lineno only": ":6475",
+ "invalid path": "path validity is not validated",
+ "path w/ colon": r"c:\temp\lib.py",
+ "path w/ colon & lineno": r"c:\temp\lib.py:1234567890",
+ "no source": None,
+ "nön-äscii": "hyvä esimerkki",
+ "nön-äscii utf-8": b"\xe7\xa6\x8f:88",
+ "invalid source": 666,
+ }
def get_keyword_names(self):
return list(self.keywords)
@@ -281,10 +295,10 @@ def get_keyword_source(self, name):
class _KeywordInfo:
- doc_template = 'Keyword documentation for %s'
+ doc_template = "Keyword documentation for {}"
def __init__(self, name, argspec, doc=None):
- self.doc = doc or self.doc_template % name
+ self.doc = doc or self.doc_template.format(name)
self.argspec = argspec
@@ -297,7 +311,7 @@ def get_keyword_documentation(self, name, invalid_arg):
class InvalidGetArgsDynamicLibrary(ArgDocDynamicLibrary):
def get_keyword_arguments(self, name):
- 1/0
+ 1 / 0
class InvalidAttributeDynamicLibrary(ArgDocDynamicLibrary):
@@ -313,6 +327,7 @@ def wraps(x):
@functools.wraps(x)
def wrapper(*a, **k):
return x(*a, **k)
+
return wrapper
@@ -332,7 +347,8 @@ def no_wrapper(self):
def wrapper(self):
pass
- if hasattr(functools, 'lru_cache'):
+ if hasattr(functools, "lru_cache"):
+
@functools.lru_cache()
def external(self):
pass
@@ -346,4 +362,4 @@ def __lt__(self, other):
return True
-NoClassDefinition = type('NoClassDefinition', (), {})
+NoClassDefinition = type("NoClassDefinition", (), {})
diff --git a/atest/testresources/testlibs/dynlibs.py b/atest/testresources/testlibs/dynlibs.py
index c23357456a5..9c5c8cfe8bb 100644
--- a/atest/testresources/testlibs/dynlibs.py
+++ b/atest/testresources/testlibs/dynlibs.py
@@ -6,36 +6,45 @@ def get_keyword_names(self):
def run_keyword(self, name, *args):
return None
+
class StaticDocsLib(_BaseDynamicLibrary):
"""This is lib intro."""
+
def __init__(self, some=None, args=[]):
"""Init doc."""
+
class DynamicDocsLib(_BaseDynamicLibrary):
- def __init__(self, *args): pass
+ def __init__(self, *args):
+ pass
def get_keyword_documentation(self, name):
- if name == '__intro__':
- return 'Dynamic intro doc.'
- if name == '__init__':
- return 'Dynamic init doc.'
- return ''
+ if name == "__intro__":
+ return "Dynamic intro doc."
+ if name == "__init__":
+ return "Dynamic init doc."
+ return ""
+
class StaticAndDynamicDocsLib(_BaseDynamicLibrary):
"""This is static doc."""
+
def __init__(self, an_arg=None):
"""This is static doc."""
+
def get_keyword_documentation(self, name):
- if name == '__intro__':
- return 'dynamic override'
- if name == '__init__':
- return 'dynamic override'
- return ''
+ if name == "__intro__":
+ return "dynamic override"
+ if name == "__init__":
+ return "dynamic override"
+ return ""
+
class FailingDynamicDocLib(_BaseDynamicLibrary):
"""intro-o-o"""
+
def __init__(self):
"""initoo-o-o"""
+
def get_keyword_documentation(self, name):
raise RuntimeError(f"Failing in 'get_keyword_documentation' with '{name}'.")
-
diff --git a/atest/testresources/testlibs/libmodule.py b/atest/testresources/testlibs/libmodule.py
index 157eeafc4b4..4c7aadce80b 100644
--- a/atest/testresources/testlibs/libmodule.py
+++ b/atest/testresources/testlibs/libmodule.py
@@ -1,11 +1,10 @@
class LibClass1:
-
+
def verify_libclass1(self):
- return 'LibClass 1 works'
-
+ return "LibClass 1 works"
+
class LibClass2:
def verify_libclass2(self):
- return 'LibClass 2 works also'
-
\ No newline at end of file
+ return "LibClass 2 works also"
diff --git a/atest/testresources/testlibs/libraryscope.py b/atest/testresources/testlibs/libraryscope.py
index 9d56cd7ff99..f5191a19912 100644
--- a/atest/testresources/testlibs/libraryscope.py
+++ b/atest/testresources/testlibs/libraryscope.py
@@ -8,12 +8,13 @@ def register(self, name):
def should_be_registered(self, *expected):
if self.registered != set(expected):
- raise AssertionError('Wrong registered: %s != %s'
- % (sorted(self.registered), sorted(expected)))
+ raise AssertionError(
+ f"Wrong registered: {sorted(self.registered)} != {sorted(expected)}"
+ )
class Global(_BaseLib):
- ROBOT_LIBRARY_SCOPE = 'global'
+ ROBOT_LIBRARY_SCOPE = "global"
initializations = 0
def __init__(self):
@@ -27,28 +28,28 @@ def should_be_registered(self, *expected):
class Suite(_BaseLib):
- ROBOT_LIBRARY_SCOPE = 'SUITE'
+ ROBOT_LIBRARY_SCOPE = "SUITE"
class TestSuite(_BaseLib):
- ROBOT_LIBRARY_SCOPE = 'TEST_SUITE'
+ ROBOT_LIBRARY_SCOPE = "TEST_SUITE"
class Test(_BaseLib):
- ROBOT_LIBRARY_SCOPE = 'TeSt'
+ ROBOT_LIBRARY_SCOPE = "TeSt"
class TestCase(_BaseLib):
- ROBOT_LIBRARY_SCOPE = 'TeSt CAse'
+ ROBOT_LIBRARY_SCOPE = "TeSt CAse"
class Task(_BaseLib):
# Any non-recognized value is mapped to TEST scope.
- ROBOT_LIBRARY_SCOPE = 'TASK'
+ ROBOT_LIBRARY_SCOPE = "TASK"
class InvalidValue(_BaseLib):
- ROBOT_LIBRARY_SCOPE = 'invalid'
+ ROBOT_LIBRARY_SCOPE = "invalid"
class InvalidEmpty(_BaseLib):
diff --git a/atest/testresources/testlibs/libswithargs.py b/atest/testresources/testlibs/libswithargs.py
index a19e4bcea20..7749958e1a6 100644
--- a/atest/testresources/testlibs/libswithargs.py
+++ b/atest/testresources/testlibs/libswithargs.py
@@ -10,7 +10,7 @@ def get_args(self):
class Defaults:
- def __init__(self, mandatory, default1='value', default2=None):
+ def __init__(self, mandatory, default1="value", default2=None):
self.mandatory = mandatory
self.default1 = default1
self.default2 = default2
@@ -22,11 +22,10 @@ def get_args(self):
class Varargs(Mandatory):
def __init__(self, mandatory, *varargs):
- Mandatory.__init__(self, mandatory, ' '.join(str(a) for a in varargs))
+ super().__init__(mandatory, " ".join(str(a) for a in varargs))
class Mixed(Defaults):
def __init__(self, mandatory, default=42, *extra):
- Defaults.__init__(self, mandatory, default,
- ' '.join(str(a) for a in extra))
+ super().__init__(mandatory, default, " ".join(str(a) for a in extra))
diff --git a/atest/testresources/testlibs/module_library.py b/atest/testresources/testlibs/module_library.py
index 3c24dfc2042..7e6d16d165b 100644
--- a/atest/testresources/testlibs/module_library.py
+++ b/atest/testresources/testlibs/module_library.py
@@ -1,39 +1,48 @@
-ROBOT_LIBRARY_SCOPE = 'Test Suite' # this should be ignored
-__version__ = 'test' # this should be used as version of this library
+ROBOT_LIBRARY_SCOPE = "Test Suite" # this should be ignored
+__version__ = "test" # this should be used as version of this library
def passing():
pass
+
def failing():
- raise AssertionError('This is a failing keyword from module library')
+ raise AssertionError("This is a failing keyword from module library")
+
def logging():
- print('Hello from module library')
- print('*WARN* WARNING!')
+ print("Hello from module library")
+ print("*WARN* WARNING!")
+
def returning():
- return 'Hello from module library'
+ return "Hello from module library"
+
def argument(arg):
- assert arg == 'Hello', "Expected 'Hello', got '%s'" % arg
+ assert arg == "Hello", f"Expected 'Hello', got '{arg}'"
+
def many_arguments(arg1, arg2, arg3):
- assert arg1 == arg2 == arg3, ("All arguments should have been equal, got: "
- "%s, %s and %s") % (arg1, arg2, arg3)
+ msg = f"All arguments should have been equal, got: {arg1}, {arg2} and {arg3}"
+ assert arg1 == arg2 == arg3, msg
+
-def default_arguments(arg1, arg2='Hi', arg3='Hello'):
+def default_arguments(arg1, arg2="Hi", arg3="Hello"):
many_arguments(arg1, arg2, arg3)
+
def variable_arguments(*args):
return sum([int(arg) for arg in args])
-attribute = 'This is not a keyword!'
+
+attribute = "This is not a keyword!"
+
class NotLibrary:
def two_arguments(self, arg1, arg2):
- msg = "Arguments should have been unequal, both were '%s'" % arg1
+ msg = f"Arguments should have been unequal, both were '{arg1}'"
assert arg1 != arg2, msg
def not_keyword(self):
@@ -46,9 +55,10 @@ def not_keyword(self):
lambda_keyword = lambda arg: int(arg) + 1
lambda_keyword_with_two_args = lambda x, y: int(x) / int(y)
+
def _not_keyword():
pass
+
def module_library():
return "It should be OK to have an attribute with same name as the module"
-
diff --git a/atest/testresources/testlibs/newstyleclasses.py b/atest/testresources/testlibs/newstyleclasses.py
index 6915cb74ed7..61b00068b96 100644
--- a/atest/testresources/testlibs/newstyleclasses.py
+++ b/atest/testresources/testlibs/newstyleclasses.py
@@ -3,15 +3,15 @@ class NewStyleClassLibrary:
def mirror(self, arg):
arg = list(arg)
arg.reverse()
- return ''.join(arg)
+ return "".join(arg)
@property
def property_getter(self):
- raise SystemExit('This should not be called, ever!!!')
+ raise SystemExit("This should not be called, ever!!!")
@property
def _property_getter(self):
- raise SystemExit('This should not be called, ever!!!')
+ raise SystemExit("This should not be called, ever!!!")
class NewStyleClassArgsLibrary:
@@ -23,7 +23,7 @@ def __init__(self, param):
class MyMetaClass(type):
def __new__(cls, name, bases, ns):
- ns['kw_created_by_metaclass'] = lambda self, arg: arg.upper()
+ ns["kw_created_by_metaclass"] = lambda self, arg: arg.upper()
return type.__new__(cls, name, bases, ns)
def method_in_metaclass(cls):
@@ -33,4 +33,4 @@ def method_in_metaclass(cls):
class MetaClassLibrary(metaclass=MyMetaClass):
def greet(self, name):
- return 'Hello %s!' % name
+ return f"Hello {name}!"
diff --git a/atest/testresources/testlibs/pythonmodule/__init__.py b/atest/testresources/testlibs/pythonmodule/__init__.py
index 94263afeda6..9b9ed1bf4dd 100644
--- a/atest/testresources/testlibs/pythonmodule/__init__.py
+++ b/atest/testresources/testlibs/pythonmodule/__init__.py
@@ -1,8 +1,10 @@
class SomeObject:
pass
+
some_object = SomeObject()
-some_string = 'Hello, World!'
+some_string = "Hello, World!"
+
def keyword():
pass
diff --git a/atest/testresources/testlibs/pythonmodule/library.py b/atest/testresources/testlibs/pythonmodule/library.py
index d3b3c3a148f..d41a6a6dcf6 100644
--- a/atest/testresources/testlibs/pythonmodule/library.py
+++ b/atest/testresources/testlibs/pythonmodule/library.py
@@ -1,5 +1,5 @@
library = "It should be OK to have an attribute with same name as the module"
-def keyword_from_submodule(arg='World'):
- return "Hello, %s!" % arg
+def keyword_from_submodule(arg="World"):
+ return f"Hello, {arg}!"
diff --git a/atest/testresources/testlibs/pythonmodule/submodule/sublib.py b/atest/testresources/testlibs/pythonmodule/submodule/sublib.py
index 0474e5ee424..ec65ae47b4e 100644
--- a/atest/testresources/testlibs/pythonmodule/submodule/sublib.py
+++ b/atest/testresources/testlibs/pythonmodule/submodule/sublib.py
@@ -1,9 +1,8 @@
def keyword_from_deeper_submodule():
- return 'hi again'
+ return "hi again"
class Sub:
def keyword_from_class_in_deeper_submodule(self):
- return 'bye'
-
+ return "bye"
diff --git a/doc/api/autodoc/robot.running.timeouts.rst b/doc/api/autodoc/robot.running.timeouts.rst
index 7c0298d77cc..a3457e57afe 100644
--- a/doc/api/autodoc/robot.running.timeouts.rst
+++ b/doc/api/autodoc/robot.running.timeouts.rst
@@ -9,6 +9,14 @@ robot.running.timeouts package
Submodules
----------
+robot.running.timeouts.nosupport module
+---------------------------------------
+
+.. automodule:: robot.running.timeouts.nosupport
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
robot.running.timeouts.posix module
-----------------------------------
diff --git a/doc/api/conf.py b/doc/api/conf.py
index 6044ae25e30..2fd16a65088 100644
--- a/doc/api/conf.py
+++ b/doc/api/conf.py
@@ -27,7 +27,7 @@
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode']
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx_rtd_theme']
autoclass_content = 'both'
autodoc_default_flags = ['members', 'undoc-members', 'inherited-members']
autodoc_member_order = 'bysource'
@@ -100,7 +100,7 @@
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
-#html_theme = 'classic'
+html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
diff --git a/doc/api/requirements.txt b/doc/api/requirements.txt
new file mode 100644
index 00000000000..07084c462af
--- /dev/null
+++ b/doc/api/requirements.txt
@@ -0,0 +1,2 @@
+sphinx
+sphinx-rtd-theme
\ No newline at end of file
diff --git a/doc/images/architecture.svg b/doc/images/architecture.svg
index 73dcf703b39..e3a3440f161 100644
--- a/doc/images/architecture.svg
+++ b/doc/images/architecture.svg
@@ -1,271 +1,142 @@
diff --git a/doc/releasenotes/rf-3.1.rst b/doc/releasenotes/rf-3.1.rst
index d228a9fd658..def0c692ad8 100644
--- a/doc/releasenotes/rf-3.1.rst
+++ b/doc/releasenotes/rf-3.1.rst
@@ -101,7 +101,7 @@ Automatic argument conversion
By default all arguments that are not specified as variables are given to
Python based keywords as Unicode strings. This includes cases like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -114,7 +114,7 @@ then converts arguments to the specified types automatically.
When using Python 3, it is possible to use `function annotations`__ to
explicitly specify types (`#2890`_):
-.. sourcecode:: python
+.. code:: python
def example_keyword(count: int, case_insensitive: bool = True):
if case_insensitive:
@@ -125,7 +125,7 @@ decorator (`#2947`_) that works also with Python 2. It is possible both
to map argument names to types using a dictionary and to use a list mapping
arguments to types based on position:
-.. sourcecode:: python
+.. code:: python
@keyword(types={'count': int, 'case_insensitive': bool})
def example_keyword(count, case_insensitive=True):
@@ -140,7 +140,7 @@ arguments to types based on position:
If an argument has no explicit type specified, Robot Framework still tries
to get the type implicitly from an argument default values (`#2932`_):
-.. sourcecode:: python
+.. code:: python
def example_keyword(count=-1, case_insensitive=True):
if case_insensitive:
@@ -164,13 +164,13 @@ user keywords (`#2896`_), and dynamic libraries (`#2897`_).
With Python 3 libraries this syntax could be used, for example, like this:
-.. sourcecode:: python
+.. code:: python
def sort_words(*words, case_sensitive=False):
key = str.lower if case_sensitive else None
return sorted(words, key=key)
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -179,7 +179,7 @@ With Python 3 libraries this syntax could be used, for example, like this:
User keywords using the new syntax could look like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
With Varargs
diff --git a/doc/releasenotes/rf-3.1a2.rst b/doc/releasenotes/rf-3.1a2.rst
index d97ece6df5d..17ba79f44da 100644
--- a/doc/releasenotes/rf-3.1a2.rst
+++ b/doc/releasenotes/rf-3.1a2.rst
@@ -106,7 +106,7 @@ Automatic argument conversion
By default all arguments that are not specified as variables are given to
Python based keywords as Unicode strings. This includes cases like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -120,7 +120,7 @@ automatically.
When using Python 3, it is possible to use `function annotations`__ to
explicitly specify types (`#2890`_):
-.. sourcecode:: python
+.. code:: python
def example_keyword(count: int, case_insensitive: bool = True):
if case_insensitive:
@@ -131,7 +131,7 @@ decorator (`#2947`_) that works both with Python 2 and Python 3. It is
possible both to map argument names to types using a dictionary and to
use a list mapping arguments to types based on position:
-.. sourcecode:: python
+.. code:: python
@keyword(types={'count': int, 'case_insensitive': bool})
def example_keyword(count, case_insensitive=True):
@@ -146,7 +146,7 @@ use a list mapping arguments to types based on position:
If an argument has no explicit type specified, Robot Framework still tries
to get the type implicitly from an argument default values (`#2932`_):
-.. sourcecode:: python
+.. code:: python
def example_keyword(count=-1, case_insensitive=True):
if case_insensitive:
@@ -164,13 +164,13 @@ user keywords (`#2896`_), and with dynamic libraries (`#2897`_).
With Python 3 libraries this syntax could be used, for example, like this:
-.. sourcecode:: python
+.. code:: python
def sort_words(*words, case_sensitive=False):
key = str.lower if case_sensitive else None
return sorted(words, key=key)
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -179,7 +179,7 @@ With Python 3 libraries this syntax could be used, for example, like this:
User keywords using the new syntax could look like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
With Varargs
diff --git a/doc/releasenotes/rf-3.1b1.rst b/doc/releasenotes/rf-3.1b1.rst
index 70b544ff735..6a2e2ff10bb 100644
--- a/doc/releasenotes/rf-3.1b1.rst
+++ b/doc/releasenotes/rf-3.1b1.rst
@@ -104,7 +104,7 @@ Automatic argument conversion
By default all arguments that are not specified as variables are given to
Python based keywords as Unicode strings. This includes cases like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -117,7 +117,7 @@ then converts arguments to the specified types automatically.
When using Python 3, it is possible to use `function annotations`__ to
explicitly specify types (`#2890`_):
-.. sourcecode:: python
+.. code:: python
def example_keyword(count: int, case_insensitive: bool = True):
if case_insensitive:
@@ -128,7 +128,7 @@ decorator (`#2947`_) that works also with Python 2. It is possible both
to map argument names to types using a dictionary and to use a list mapping
arguments to types based on position:
-.. sourcecode:: python
+.. code:: python
@keyword(types={'count': int, 'case_insensitive': bool})
def example_keyword(count, case_insensitive=True):
@@ -143,7 +143,7 @@ arguments to types based on position:
If an argument has no explicit type specified, Robot Framework still tries
to get the type implicitly from an argument default values (`#2932`_):
-.. sourcecode:: python
+.. code:: python
def example_keyword(count=-1, case_insensitive=True):
if case_insensitive:
@@ -161,13 +161,13 @@ user keywords (`#2896`_), and with dynamic libraries (`#2897`_).
With Python 3 libraries this syntax could be used, for example, like this:
-.. sourcecode:: python
+.. code:: python
def sort_words(*words, case_sensitive=False):
key = str.lower if case_sensitive else None
return sorted(words, key=key)
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -176,7 +176,7 @@ With Python 3 libraries this syntax could be used, for example, like this:
User keywords using the new syntax could look like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
With Varargs
diff --git a/doc/releasenotes/rf-3.1rc1.rst b/doc/releasenotes/rf-3.1rc1.rst
index f8799c678d4..32beb48685b 100644
--- a/doc/releasenotes/rf-3.1rc1.rst
+++ b/doc/releasenotes/rf-3.1rc1.rst
@@ -103,7 +103,7 @@ Automatic argument conversion
By default all arguments that are not specified as variables are given to
Python based keywords as Unicode strings. This includes cases like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -116,7 +116,7 @@ then converts arguments to the specified types automatically.
When using Python 3, it is possible to use `function annotations`__ to
explicitly specify types (`#2890`_):
-.. sourcecode:: python
+.. code:: python
def example_keyword(count: int, case_insensitive: bool = True):
if case_insensitive:
@@ -127,7 +127,7 @@ decorator (`#2947`_) that works also with Python 2. It is possible both
to map argument names to types using a dictionary and to use a list mapping
arguments to types based on position:
-.. sourcecode:: python
+.. code:: python
@keyword(types={'count': int, 'case_insensitive': bool})
def example_keyword(count, case_insensitive=True):
@@ -142,7 +142,7 @@ arguments to types based on position:
If an argument has no explicit type specified, Robot Framework still tries
to get the type implicitly from an argument default values (`#2932`_):
-.. sourcecode:: python
+.. code:: python
def example_keyword(count=-1, case_insensitive=True):
if case_insensitive:
@@ -166,13 +166,13 @@ user keywords (`#2896`_), and dynamic libraries (`#2897`_).
With Python 3 libraries this syntax could be used, for example, like this:
-.. sourcecode:: python
+.. code:: python
def sort_words(*words, case_sensitive=False):
key = str.lower if case_sensitive else None
return sorted(words, key=key)
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -181,7 +181,7 @@ With Python 3 libraries this syntax could be used, for example, like this:
User keywords using the new syntax could look like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
With Varargs
diff --git a/doc/releasenotes/rf-3.1rc2.rst b/doc/releasenotes/rf-3.1rc2.rst
index 6d4caa9e870..4a61976322d 100644
--- a/doc/releasenotes/rf-3.1rc2.rst
+++ b/doc/releasenotes/rf-3.1rc2.rst
@@ -105,7 +105,7 @@ Automatic argument conversion
By default all arguments that are not specified as variables are given to
Python based keywords as Unicode strings. This includes cases like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -118,7 +118,7 @@ then converts arguments to the specified types automatically.
When using Python 3, it is possible to use `function annotations`__ to
explicitly specify types (`#2890`_):
-.. sourcecode:: python
+.. code:: python
def example_keyword(count: int, case_insensitive: bool = True):
if case_insensitive:
@@ -129,7 +129,7 @@ decorator (`#2947`_) that works also with Python 2. It is possible both
to map argument names to types using a dictionary and to use a list mapping
arguments to types based on position:
-.. sourcecode:: python
+.. code:: python
@keyword(types={'count': int, 'case_insensitive': bool})
def example_keyword(count, case_insensitive=True):
@@ -144,7 +144,7 @@ arguments to types based on position:
If an argument has no explicit type specified, Robot Framework still tries
to get the type implicitly from an argument default values (`#2932`_):
-.. sourcecode:: python
+.. code:: python
def example_keyword(count=-1, case_insensitive=True):
if case_insensitive:
@@ -168,13 +168,13 @@ user keywords (`#2896`_), and dynamic libraries (`#2897`_).
With Python 3 libraries this syntax could be used, for example, like this:
-.. sourcecode:: python
+.. code:: python
def sort_words(*words, case_sensitive=False):
key = str.lower if case_sensitive else None
return sorted(words, key=key)
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -183,7 +183,7 @@ With Python 3 libraries this syntax could be used, for example, like this:
User keywords using the new syntax could look like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
With Varargs
diff --git a/doc/releasenotes/rf-4.0.rst b/doc/releasenotes/rf-4.0.rst
index 98908814e47..492a141d027 100644
--- a/doc/releasenotes/rf-4.0.rst
+++ b/doc/releasenotes/rf-4.0.rst
@@ -182,7 +182,7 @@ block is highly recommended but not mandatory.
In the following example keywords `Some keyword` and `Another keyword`
are executed if `${rc}` is greater than zero:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -210,7 +210,7 @@ not true.
In this example `Some keyword` is executed if `${rc}` is greater than
zero and `Another keyword` is executed otherwise:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -237,7 +237,7 @@ ELSE IF branches and it is executed if all conditions are false.
In the following example different keyword is executed depending on the value
of the `${direction}` variable:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
diff --git a/doc/releasenotes/rf-4.0b1.rst b/doc/releasenotes/rf-4.0b1.rst
index e4231cd73c7..e3702634ad7 100644
--- a/doc/releasenotes/rf-4.0b1.rst
+++ b/doc/releasenotes/rf-4.0b1.rst
@@ -178,7 +178,7 @@ block is highly recommended but not mandatory.
In the following example keywords `Some keyword` and `Another keyword`
are executed if `${rc}` is greater than zero:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -206,7 +206,7 @@ not true.
In this example `Some keyword` is executed if `${rc}` is greater than
zero and `Another keyword` is executed otherwise:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -229,7 +229,7 @@ and remaining `ELSE IF` branches are ignored. An optional `ELSE` branch can foll
In the following example different keyword is executed depending on is `${rc}` positive,
negative, zero, or something else like a string or `None`:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
diff --git a/doc/releasenotes/rf-4.0b2.rst b/doc/releasenotes/rf-4.0b2.rst
index f5ab31a07b0..dd5c587e766 100644
--- a/doc/releasenotes/rf-4.0b2.rst
+++ b/doc/releasenotes/rf-4.0b2.rst
@@ -179,7 +179,7 @@ block is highly recommended but not mandatory.
In the following example keywords `Some keyword` and `Another keyword`
are executed if `${rc}` is greater than zero:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -207,7 +207,7 @@ not true.
In this example `Some keyword` is executed if `${rc}` is greater than
zero and `Another keyword` is executed otherwise:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -230,7 +230,7 @@ ELSE IF branches and it is executed if all conditions are false.
In the following example different keyword is executed depending on is `${rc}`
positive, negative, zero, or something else like a string or `None`:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
diff --git a/doc/releasenotes/rf-4.0b3.rst b/doc/releasenotes/rf-4.0b3.rst
index 8695a30f047..b341118327b 100644
--- a/doc/releasenotes/rf-4.0b3.rst
+++ b/doc/releasenotes/rf-4.0b3.rst
@@ -179,7 +179,7 @@ block is highly recommended but not mandatory.
In the following example keywords `Some keyword` and `Another keyword`
are executed if `${rc}` is greater than zero:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -207,7 +207,7 @@ not true.
In this example `Some keyword` is executed if `${rc}` is greater than
zero and `Another keyword` is executed otherwise:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -230,7 +230,7 @@ ELSE IF branches and it is executed if all conditions are false.
In the following example different keyword is executed depending on is `${rc}`
positive, negative, zero, or something else like a string or `None`:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
diff --git a/doc/releasenotes/rf-4.0rc1.rst b/doc/releasenotes/rf-4.0rc1.rst
index 1466c3e85d8..348d8408d00 100644
--- a/doc/releasenotes/rf-4.0rc1.rst
+++ b/doc/releasenotes/rf-4.0rc1.rst
@@ -178,7 +178,7 @@ block is highly recommended but not mandatory.
In the following example keywords `Some keyword` and `Another keyword`
are executed if `${rc}` is greater than zero:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -206,7 +206,7 @@ not true.
In this example `Some keyword` is executed if `${rc}` is greater than
zero and `Another keyword` is executed otherwise:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -229,7 +229,7 @@ ELSE IF branches and it is executed if all conditions are false.
In the following example different keyword is executed depending on is `${rc}`
positive, negative, zero, or something else like a string or `None`:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
diff --git a/doc/releasenotes/rf-4.0rc2.rst b/doc/releasenotes/rf-4.0rc2.rst
index df97faa965b..e0f3f4a67a5 100644
--- a/doc/releasenotes/rf-4.0rc2.rst
+++ b/doc/releasenotes/rf-4.0rc2.rst
@@ -180,7 +180,7 @@ block is highly recommended but not mandatory.
In the following example keywords `Some keyword` and `Another keyword`
are executed if `${rc}` is greater than zero:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -208,7 +208,7 @@ not true.
In this example `Some keyword` is executed if `${rc}` is greater than
zero and `Another keyword` is executed otherwise:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -231,7 +231,7 @@ ELSE IF branches and it is executed if all conditions are false.
In the following example different keyword is executed depending on is `${rc}`
positive, negative, zero, or something else like a string or `None`:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
diff --git a/doc/releasenotes/rf-5.0.rst b/doc/releasenotes/rf-5.0.rst
index 04a0fb1c466..565272b56d0 100644
--- a/doc/releasenotes/rf-5.0.rst
+++ b/doc/releasenotes/rf-5.0.rst
@@ -74,7 +74,7 @@ Catching exceptions with `EXCEPT`
The basic `TRY/EXCEPT` syntax can be used to handle failures based on
error messages:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
First example
@@ -99,7 +99,7 @@ multiple messages to match, and such a branch is executed if any of its messages
match. In all these cases messages can be specified using variables in addition
to literal strings.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Multiple EXCEPT branches
@@ -124,7 +124,7 @@ It is also possible to have an `EXCEPT` without messages, in which case it match
any error. There can be only one such `EXCEPT` and it must follow possible
other `EXCEPT` branches:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Match any error
@@ -154,7 +154,7 @@ values for the type are `GLOB`, `REGEXP`, `START` and `LITERAL` (default) and
all values are case insensitive. If an `EXCEPT` has multiple messages, the type
applies to all of them.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Glob pattern
@@ -205,7 +205,7 @@ occurred. Robot Framework supports that by making it possible to capture
the error message into a variable by adding `AS ${var}` at the
end of the `EXCEPT` statement:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Capture error
@@ -224,7 +224,7 @@ Optional `ELSE` branches make it possible to execute keywords if there is no err
There can be only one `ELSE` branch and it is allowed only after one or more
`EXCEPT` branches:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
ELSE branch
@@ -248,7 +248,7 @@ and the `TRY/EXCEPT/ELSE` structure fails.
To handle both the case when there is any error and when there is no error,
it is possible to use an `EXCEPT` without any message in combination with an `ELSE`:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Handle everything
@@ -269,7 +269,7 @@ after a keyword execution somewhat similarly as teardowns. There can be only one
`FINALLY` branch and it must always be last. They can be used in combination with
`EXCEPT` and `ELSE` branches and having also `TRY/FINALLY` structure is possible:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
TRY/EXCEPT/ELSE/FINALLY
@@ -316,7 +316,7 @@ appendix in the User Guide.
Example:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Variables ***
${x} 10
@@ -339,7 +339,7 @@ control statements are often used in combination with the new `inline IF`_ synta
Example:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Variables ***
${x} 10
@@ -387,7 +387,7 @@ are positive integers denoting iteration count and "time strings" like `10s` or
altogether by using `NONE` (case-insensitive). All these options are illustrated
by the examples below.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Limit as iteration count
@@ -418,7 +418,7 @@ using the new inline `IF` syntax (`#4093`_) where the statement to execute follo
the `IF` marker and condition directly and no `END` marker is needed. For example,
the following two keywords are equivalent:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keyword ***
Normal IF
@@ -435,7 +435,7 @@ the following two keywords are equivalent:
The inline `IF` syntax supports also `ELSE` and `ELSE IF` branches:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keyword ***
Inline IF/ELSE
@@ -457,7 +457,7 @@ to assign must be before the starting `IF`. Otherwise the logic is exactly
the same as when assigning variables based on keyword return values. If
assignment is used and no branch is run, the variable gets value `None`.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keyword ***
Inline IF/ELSE with assignment
@@ -475,7 +475,7 @@ New `BREAK` and `CONTINUE` statements (`#4079`_) were already used in WHILE_
examples above. In addition to that they work with the old `FOR` loops and with
both loops they are often combined with `inline IF`_:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -498,7 +498,7 @@ It can be used for returning values when the keyword has been executed like
when using the old `[Return]` setting, and also for returning prematurely like
the old `Return From Keyword` keyword supports:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
Return at the end
@@ -553,7 +553,7 @@ Let's assume we wanted to create a keyword that accepts date_ objects for
users in Finland where the commonly used date format is `dd.mm.yyyy`.
The usage could look something like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -563,7 +563,7 @@ Automatic argument conversion supports dates, but it expects them
to be in `yyyy-mm-dd` format so it will not work. A solution is creating
a custom converter and registering it to handle date_ conversion:
-.. sourcecode:: python
+.. code:: python
from datetime import date
@@ -599,7 +599,7 @@ using `regular expressions`__ makes it possible to validate both that the input
has dots (`.`) in correct places and that date parts contain correct amount
of digits:
-.. sourcecode:: python
+.. code:: python
from datetime import date
import re
@@ -643,7 +643,7 @@ the value type, but if the converter only accepts certain types, it is typically
easier to just restrict the value to that type. Doing it requires only adding
appropriate type hint to the converter:
-.. sourcecode:: python
+.. code:: python
def parse_fi_date(value: str):
# ...
@@ -659,7 +659,7 @@ as a Union_. For example, if we wanted to enhance our keyword to accept also
integers so that they would be considered seconds since the `Unix epoch`__,
we could change the converter like this:
-.. sourcecode:: python
+.. code:: python
from datetime import date
import re
@@ -693,7 +693,7 @@ A problem with the earlier example is that date_ objects could only be given
in `dd.mm.yyyy` format. It would not work if there was a need to
support dates in different formats like in this example:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -704,7 +704,7 @@ support dates in different formats like in this example:
A solution to this problem is creating custom types instead of overriding
the default date_ conversion:
-.. sourcecode:: python
+.. code:: python
from datetime import date
import re
@@ -776,7 +776,7 @@ not have any documentation, documentation is got from the type. Both of these
approaches to add documentation to converters in the previous example thus
produce the same result:
-.. sourcecode:: python
+.. code:: python
class FiDate(date):
@@ -871,7 +871,7 @@ Loop control keywords cannot be used inside keywords
directly inside a FOR loop, not in keywords used by loops (`#4185`_). For example,
this is not anymore supported:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
Looping
diff --git a/doc/releasenotes/rf-5.0a1.rst b/doc/releasenotes/rf-5.0a1.rst
index 0caace91e4e..1b555914e54 100644
--- a/doc/releasenotes/rf-5.0a1.rst
+++ b/doc/releasenotes/rf-5.0a1.rst
@@ -80,7 +80,7 @@ Catching exceptions with `EXCEPT`
The basic `TRY/EXCEPT` syntax can be used to handle failures based on
error messages:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
First example
@@ -105,7 +105,7 @@ multiple messages to match, and such a branch is executed if any of its messages
match. In all these cases messages can be specified using variables in addition
to literal strings.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Multiple EXCEPT branches
@@ -130,7 +130,7 @@ It is also possible to have an `EXCEPT` without messages, in which case it match
any error. There can be only one such `EXCEPT` and it must follow possible
other `EXCEPT` branches:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Match any error
@@ -161,7 +161,7 @@ messages, possible prefixes apply only to messages they are attached to, not to
other messages. The prefix must always be specified explicitly and cannot come
from a variable.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Glob pattern
@@ -214,7 +214,7 @@ occurred. Robot Framework supports that by making it possible to capture
the error message into a variable by adding `AS ${var}` at the
end of the `EXCEPT` statement:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Capture error
@@ -235,7 +235,7 @@ Optional `ELSE` branches make it possible to execute keywords if there is no err
There can be only one `ELSE` branch and it is allowed only after one or more
`EXCEPT` branches:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
ELSE branch
@@ -259,7 +259,7 @@ and the `TRY/EXCEPT/ELSE` structure fails.
To handle both the case when there is any error and when there is no error,
it is possible to use an `EXCEPT` without any message in combination with an `ELSE`:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Handle everything
@@ -280,7 +280,7 @@ after a keyword execution somewhat similarly as teardowns. There can be only one
`FINALLY` branch and it must always be last. They can be used in combination with
`EXCEPT` and `ELSE` branches and having also `TRY/FINALLY` structure is possible:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
TRY/EXCEPT/ELSE/FINALLY
@@ -322,7 +322,7 @@ appendix in the User Guide.
Examples:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Variables ***
${x} 10
@@ -357,7 +357,7 @@ using the new inline `IF` syntax (`#4093`_) where the statement to execute follo
the `IF` marker and condition directly and no `END` marker is needed. For example,
the following two keywords are equivalent:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keyword ***
Normal IF
@@ -374,7 +374,7 @@ the following two keywords are equivalent:
The inline `IF` syntax supports also `ELSE` and `ELSE IF` branches:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keyword ***
Inline IF/ELSE
@@ -396,7 +396,7 @@ to assign must be before the starting `IF`. Otherwise the logic is exactly
the same as when assigning variables based on keyword return values. If
assignment is used and no branch is run, the variable gets value `None`.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keyword ***
Inline IF/ELSE with assignment
@@ -414,7 +414,7 @@ New `BREAK` and `CONTINUE` statements (`#4079`_) were already used in WHILE_
examples above. In addition to that they work with the old `FOR` loops and with
both loops they are often combined with `inline IF`_:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -437,7 +437,7 @@ It can be used for returning values when the keyword has been executed like
when using the old `[Return]` setting, and also for returning prematurely like
the old `Return From Keyword` keyword supports:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
Return at the end
@@ -492,7 +492,7 @@ Let's assume we wanted to create a keyword that accepts date_ objects for
users in Finland where the commonly used date format is `dd.mm.yyyy`.
The usage could look something like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -502,7 +502,7 @@ Automatic argument conversion supports dates, but it expects them
to be in `yyyy-mm-dd` format so it will not work. A solution is creating
a custom converter and registering it to handle date_ conversion:
-.. sourcecode:: python
+.. code:: python
from datetime import date
@@ -538,7 +538,7 @@ using `regular expressions`__ makes it possible to validate both that the input
has dots (`.`) in correct places and that date parts contain correct amount
of digits:
-.. sourcecode:: python
+.. code:: python
from datetime import date
import re
@@ -582,7 +582,7 @@ the value type, but if the converter only accepts certain types, it is typically
easier to just restrict the value to that type. Doing it requires only adding
appropriate type hint to the converter:
-.. sourcecode:: python
+.. code:: python
def parse_fi_date(value: str):
# ...
@@ -598,7 +598,7 @@ as a Union_. For example, if we wanted to enhance our keyword to accept also
integers so that they would be considered seconds since the `Unix epoch`__,
we could change the converter like this:
-.. sourcecode:: python
+.. code:: python
from datetime import date
import re
@@ -632,7 +632,7 @@ A problem with the earlier example is that date_ objects could only be given
in `dd.mm.yyyy` format. It would would not work if there would be need to
support dates in different formats like in this example:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -643,7 +643,7 @@ support dates in different formats like in this example:
A solution to this problem is creating custom types instead of overriding
the default date_ conversion:
-.. sourcecode:: python
+.. code:: python
from datetime import date
import re
@@ -720,7 +720,7 @@ not have any documentation, documentation is got from the type. Both of these
approaches to add documentation to converters in the previous example thus
produce the same result:
-.. sourcecode:: python
+.. code:: python
class FiDate(date):
@@ -779,7 +779,7 @@ Loop control keywords cannot be used inside keywords
directly inside a FOR loop, not in keywords used by loops (`#4185`_). For example,
this is not anymore supported:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
Looping
diff --git a/doc/releasenotes/rf-5.0b1.rst b/doc/releasenotes/rf-5.0b1.rst
index e5b8bb1d08f..fa8ea44f1d6 100644
--- a/doc/releasenotes/rf-5.0b1.rst
+++ b/doc/releasenotes/rf-5.0b1.rst
@@ -80,7 +80,7 @@ Catching exceptions with `EXCEPT`
The basic `TRY/EXCEPT` syntax can be used to handle failures based on
error messages:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
First example
@@ -105,7 +105,7 @@ multiple messages to match, and such a branch is executed if any of its messages
match. In all these cases messages can be specified using variables in addition
to literal strings.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Multiple EXCEPT branches
@@ -130,7 +130,7 @@ It is also possible to have an `EXCEPT` without messages, in which case it match
any error. There can be only one such `EXCEPT` and it must follow possible
other `EXCEPT` branches:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Match any error
@@ -161,7 +161,7 @@ messages, possible prefixes apply only to messages they are attached to, not to
other messages. The prefix must always be specified explicitly and cannot come
from a variable.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Glob pattern
@@ -214,7 +214,7 @@ occurred. Robot Framework supports that by making it possible to capture
the error message into a variable by adding `AS ${var}` at the
end of the `EXCEPT` statement:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Capture error
@@ -235,7 +235,7 @@ Optional `ELSE` branches make it possible to execute keywords if there is no err
There can be only one `ELSE` branch and it is allowed only after one or more
`EXCEPT` branches:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
ELSE branch
@@ -259,7 +259,7 @@ and the `TRY/EXCEPT/ELSE` structure fails.
To handle both the case when there is any error and when there is no error,
it is possible to use an `EXCEPT` without any message in combination with an `ELSE`:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Handle everything
@@ -280,7 +280,7 @@ after a keyword execution somewhat similarly as teardowns. There can be only one
`FINALLY` branch and it must always be last. They can be used in combination with
`EXCEPT` and `ELSE` branches and having also `TRY/FINALLY` structure is possible:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
TRY/EXCEPT/ELSE/FINALLY
@@ -322,7 +322,7 @@ appendix in the User Guide.
Examples:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Variables ***
${x} 10
@@ -357,7 +357,7 @@ using the new inline `IF` syntax (`#4093`_) where the statement to execute follo
the `IF` marker and condition directly and no `END` marker is needed. For example,
the following two keywords are equivalent:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keyword ***
Normal IF
@@ -374,7 +374,7 @@ the following two keywords are equivalent:
The inline `IF` syntax supports also `ELSE` and `ELSE IF` branches:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keyword ***
Inline IF/ELSE
@@ -396,7 +396,7 @@ to assign must be before the starting `IF`. Otherwise the logic is exactly
the same as when assigning variables based on keyword return values. If
assignment is used and no branch is run, the variable gets value `None`.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keyword ***
Inline IF/ELSE with assignment
@@ -414,7 +414,7 @@ New `BREAK` and `CONTINUE` statements (`#4079`_) were already used in WHILE_
examples above. In addition to that they work with the old `FOR` loops and with
both loops they are often combined with `inline IF`_:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -437,7 +437,7 @@ It can be used for returning values when the keyword has been executed like
when using the old `[Return]` setting, and also for returning prematurely like
the old `Return From Keyword` keyword supports:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
Return at the end
@@ -492,7 +492,7 @@ Let's assume we wanted to create a keyword that accepts date_ objects for
users in Finland where the commonly used date format is `dd.mm.yyyy`.
The usage could look something like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -502,7 +502,7 @@ Automatic argument conversion supports dates, but it expects them
to be in `yyyy-mm-dd` format so it will not work. A solution is creating
a custom converter and registering it to handle date_ conversion:
-.. sourcecode:: python
+.. code:: python
from datetime import date
@@ -538,7 +538,7 @@ using `regular expressions`__ makes it possible to validate both that the input
has dots (`.`) in correct places and that date parts contain correct amount
of digits:
-.. sourcecode:: python
+.. code:: python
from datetime import date
import re
@@ -582,7 +582,7 @@ the value type, but if the converter only accepts certain types, it is typically
easier to just restrict the value to that type. Doing it requires only adding
appropriate type hint to the converter:
-.. sourcecode:: python
+.. code:: python
def parse_fi_date(value: str):
# ...
@@ -598,7 +598,7 @@ as a Union_. For example, if we wanted to enhance our keyword to accept also
integers so that they would be considered seconds since the `Unix epoch`__,
we could change the converter like this:
-.. sourcecode:: python
+.. code:: python
from datetime import date
import re
@@ -632,7 +632,7 @@ A problem with the earlier example is that date_ objects could only be given
in `dd.mm.yyyy` format. It would would not work if there would be need to
support dates in different formats like in this example:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -643,7 +643,7 @@ support dates in different formats like in this example:
A solution to this problem is creating custom types instead of overriding
the default date_ conversion:
-.. sourcecode:: python
+.. code:: python
from datetime import date
import re
@@ -715,7 +715,7 @@ not have any documentation, documentation is got from the type. Both of these
approaches to add documentation to converters in the previous example thus
produce the same result:
-.. sourcecode:: python
+.. code:: python
class FiDate(date):
@@ -794,7 +794,7 @@ Loop control keywords cannot be used inside keywords
directly inside a FOR loop, not in keywords used by loops (`#4185`_). For example,
this is not anymore supported:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
Looping
diff --git a/doc/releasenotes/rf-5.0rc1.rst b/doc/releasenotes/rf-5.0rc1.rst
index b24592c67c6..b022dd68395 100644
--- a/doc/releasenotes/rf-5.0rc1.rst
+++ b/doc/releasenotes/rf-5.0rc1.rst
@@ -82,7 +82,7 @@ Catching exceptions with `EXCEPT`
The basic `TRY/EXCEPT` syntax can be used to handle failures based on
error messages:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
First example
@@ -107,7 +107,7 @@ multiple messages to match, and such a branch is executed if any of its messages
match. In all these cases messages can be specified using variables in addition
to literal strings.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Multiple EXCEPT branches
@@ -132,7 +132,7 @@ It is also possible to have an `EXCEPT` without messages, in which case it match
any error. There can be only one such `EXCEPT` and it must follow possible
other `EXCEPT` branches:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Match any error
@@ -162,7 +162,7 @@ values for the type are `GLOB`, `REGEXP`, `START` and `LITERAL` (default) and
all values are case insensitive. If an `EXCEPT` has multiple messages, the type
applies to all of them.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Glob pattern
@@ -216,7 +216,7 @@ occurred. Robot Framework supports that by making it possible to capture
the error message into a variable by adding `AS ${var}` at the
end of the `EXCEPT` statement:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Capture error
@@ -235,7 +235,7 @@ Optional `ELSE` branches make it possible to execute keywords if there is no err
There can be only one `ELSE` branch and it is allowed only after one or more
`EXCEPT` branches:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
ELSE branch
@@ -259,7 +259,7 @@ and the `TRY/EXCEPT/ELSE` structure fails.
To handle both the case when there is any error and when there is no error,
it is possible to use an `EXCEPT` without any message in combination with an `ELSE`:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Handle everything
@@ -280,7 +280,7 @@ after a keyword execution somewhat similarly as teardowns. There can be only one
`FINALLY` branch and it must always be last. They can be used in combination with
`EXCEPT` and `ELSE` branches and having also `TRY/FINALLY` structure is possible:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
TRY/EXCEPT/ELSE/FINALLY
@@ -327,7 +327,7 @@ appendix in the User Guide.
Example:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Variables ***
${x} 10
@@ -350,7 +350,7 @@ control statements are often used in combination with the new `inline IF`_ synta
Example:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Variables ***
${x} 10
@@ -398,7 +398,7 @@ are positive integers denoting iteration count and "time strings" like `10s` or
altogether by using `NONE` (case-insensitive). All these options are illustrated
by the examples below.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Limit as iteration count
@@ -429,7 +429,7 @@ using the new inline `IF` syntax (`#4093`_) where the statement to execute follo
the `IF` marker and condition directly and no `END` marker is needed. For example,
the following two keywords are equivalent:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keyword ***
Normal IF
@@ -446,7 +446,7 @@ the following two keywords are equivalent:
The inline `IF` syntax supports also `ELSE` and `ELSE IF` branches:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keyword ***
Inline IF/ELSE
@@ -468,7 +468,7 @@ to assign must be before the starting `IF`. Otherwise the logic is exactly
the same as when assigning variables based on keyword return values. If
assignment is used and no branch is run, the variable gets value `None`.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keyword ***
Inline IF/ELSE with assignment
@@ -486,7 +486,7 @@ New `BREAK` and `CONTINUE` statements (`#4079`_) were already used in WHILE_
examples above. In addition to that they work with the old `FOR` loops and with
both loops they are often combined with `inline IF`_:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -509,7 +509,7 @@ It can be used for returning values when the keyword has been executed like
when using the old `[Return]` setting, and also for returning prematurely like
the old `Return From Keyword` keyword supports:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
Return at the end
@@ -564,7 +564,7 @@ Let's assume we wanted to create a keyword that accepts date_ objects for
users in Finland where the commonly used date format is `dd.mm.yyyy`.
The usage could look something like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -574,7 +574,7 @@ Automatic argument conversion supports dates, but it expects them
to be in `yyyy-mm-dd` format so it will not work. A solution is creating
a custom converter and registering it to handle date_ conversion:
-.. sourcecode:: python
+.. code:: python
from datetime import date
@@ -610,7 +610,7 @@ using `regular expressions`__ makes it possible to validate both that the input
has dots (`.`) in correct places and that date parts contain correct amount
of digits:
-.. sourcecode:: python
+.. code:: python
from datetime import date
import re
@@ -654,7 +654,7 @@ the value type, but if the converter only accepts certain types, it is typically
easier to just restrict the value to that type. Doing it requires only adding
appropriate type hint to the converter:
-.. sourcecode:: python
+.. code:: python
def parse_fi_date(value: str):
# ...
@@ -670,7 +670,7 @@ as a Union_. For example, if we wanted to enhance our keyword to accept also
integers so that they would be considered seconds since the `Unix epoch`__,
we could change the converter like this:
-.. sourcecode:: python
+.. code:: python
from datetime import date
import re
@@ -704,7 +704,7 @@ A problem with the earlier example is that date_ objects could only be given
in `dd.mm.yyyy` format. It would not work if there was a need to
support dates in different formats like in this example:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -715,7 +715,7 @@ support dates in different formats like in this example:
A solution to this problem is creating custom types instead of overriding
the default date_ conversion:
-.. sourcecode:: python
+.. code:: python
from datetime import date
import re
@@ -787,7 +787,7 @@ not have any documentation, documentation is got from the type. Both of these
approaches to add documentation to converters in the previous example thus
produce the same result:
-.. sourcecode:: python
+.. code:: python
class FiDate(date):
@@ -876,7 +876,7 @@ Loop control keywords cannot be used inside keywords
directly inside a FOR loop, not in keywords used by loops (`#4185`_). For example,
this is not anymore supported:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
Looping
diff --git a/doc/releasenotes/rf-5.0rc2.rst b/doc/releasenotes/rf-5.0rc2.rst
index b77dd549104..bd792d89136 100644
--- a/doc/releasenotes/rf-5.0rc2.rst
+++ b/doc/releasenotes/rf-5.0rc2.rst
@@ -82,7 +82,7 @@ Catching exceptions with `EXCEPT`
The basic `TRY/EXCEPT` syntax can be used to handle failures based on
error messages:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
First example
@@ -107,7 +107,7 @@ multiple messages to match, and such a branch is executed if any of its messages
match. In all these cases messages can be specified using variables in addition
to literal strings.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Multiple EXCEPT branches
@@ -132,7 +132,7 @@ It is also possible to have an `EXCEPT` without messages, in which case it match
any error. There can be only one such `EXCEPT` and it must follow possible
other `EXCEPT` branches:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Match any error
@@ -162,7 +162,7 @@ values for the type are `GLOB`, `REGEXP`, `START` and `LITERAL` (default) and
all values are case insensitive. If an `EXCEPT` has multiple messages, the type
applies to all of them.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Glob pattern
@@ -216,7 +216,7 @@ occurred. Robot Framework supports that by making it possible to capture
the error message into a variable by adding `AS ${var}` at the
end of the `EXCEPT` statement:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Capture error
@@ -235,7 +235,7 @@ Optional `ELSE` branches make it possible to execute keywords if there is no err
There can be only one `ELSE` branch and it is allowed only after one or more
`EXCEPT` branches:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
ELSE branch
@@ -259,7 +259,7 @@ and the `TRY/EXCEPT/ELSE` structure fails.
To handle both the case when there is any error and when there is no error,
it is possible to use an `EXCEPT` without any message in combination with an `ELSE`:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Handle everything
@@ -280,7 +280,7 @@ after a keyword execution somewhat similarly as teardowns. There can be only one
`FINALLY` branch and it must always be last. They can be used in combination with
`EXCEPT` and `ELSE` branches and having also `TRY/FINALLY` structure is possible:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
TRY/EXCEPT/ELSE/FINALLY
@@ -327,7 +327,7 @@ appendix in the User Guide.
Example:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Variables ***
${x} 10
@@ -350,7 +350,7 @@ control statements are often used in combination with the new `inline IF`_ synta
Example:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Variables ***
${x} 10
@@ -398,7 +398,7 @@ are positive integers denoting iteration count and "time strings" like `10s` or
altogether by using `NONE` (case-insensitive). All these options are illustrated
by the examples below.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Limit as iteration count
@@ -429,7 +429,7 @@ using the new inline `IF` syntax (`#4093`_) where the statement to execute follo
the `IF` marker and condition directly and no `END` marker is needed. For example,
the following two keywords are equivalent:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keyword ***
Normal IF
@@ -446,7 +446,7 @@ the following two keywords are equivalent:
The inline `IF` syntax supports also `ELSE` and `ELSE IF` branches:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keyword ***
Inline IF/ELSE
@@ -468,7 +468,7 @@ to assign must be before the starting `IF`. Otherwise the logic is exactly
the same as when assigning variables based on keyword return values. If
assignment is used and no branch is run, the variable gets value `None`.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keyword ***
Inline IF/ELSE with assignment
@@ -486,7 +486,7 @@ New `BREAK` and `CONTINUE` statements (`#4079`_) were already used in WHILE_
examples above. In addition to that they work with the old `FOR` loops and with
both loops they are often combined with `inline IF`_:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -509,7 +509,7 @@ It can be used for returning values when the keyword has been executed like
when using the old `[Return]` setting, and also for returning prematurely like
the old `Return From Keyword` keyword supports:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
Return at the end
@@ -564,7 +564,7 @@ Let's assume we wanted to create a keyword that accepts date_ objects for
users in Finland where the commonly used date format is `dd.mm.yyyy`.
The usage could look something like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -574,7 +574,7 @@ Automatic argument conversion supports dates, but it expects them
to be in `yyyy-mm-dd` format so it will not work. A solution is creating
a custom converter and registering it to handle date_ conversion:
-.. sourcecode:: python
+.. code:: python
from datetime import date
@@ -610,7 +610,7 @@ using `regular expressions`__ makes it possible to validate both that the input
has dots (`.`) in correct places and that date parts contain correct amount
of digits:
-.. sourcecode:: python
+.. code:: python
from datetime import date
import re
@@ -654,7 +654,7 @@ the value type, but if the converter only accepts certain types, it is typically
easier to just restrict the value to that type. Doing it requires only adding
appropriate type hint to the converter:
-.. sourcecode:: python
+.. code:: python
def parse_fi_date(value: str):
# ...
@@ -670,7 +670,7 @@ as a Union_. For example, if we wanted to enhance our keyword to accept also
integers so that they would be considered seconds since the `Unix epoch`__,
we could change the converter like this:
-.. sourcecode:: python
+.. code:: python
from datetime import date
import re
@@ -704,7 +704,7 @@ A problem with the earlier example is that date_ objects could only be given
in `dd.mm.yyyy` format. It would not work if there was a need to
support dates in different formats like in this example:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -715,7 +715,7 @@ support dates in different formats like in this example:
A solution to this problem is creating custom types instead of overriding
the default date_ conversion:
-.. sourcecode:: python
+.. code:: python
from datetime import date
import re
@@ -787,7 +787,7 @@ not have any documentation, documentation is got from the type. Both of these
approaches to add documentation to converters in the previous example thus
produce the same result:
-.. sourcecode:: python
+.. code:: python
class FiDate(date):
@@ -876,7 +876,7 @@ Loop control keywords cannot be used inside keywords
directly inside a FOR loop, not in keywords used by loops (`#4185`_). For example,
this is not anymore supported:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
Looping
diff --git a/doc/releasenotes/rf-5.1b2.rst b/doc/releasenotes/rf-5.1b2.rst
index 77944c5c88c..91b84b0c54c 100644
--- a/doc/releasenotes/rf-5.1b2.rst
+++ b/doc/releasenotes/rf-5.1b2.rst
@@ -145,7 +145,7 @@ When using keywords with embedded arguments, it is pretty common that a keyword
that is used matches multiple keyword implementations. For example,
`Execute "ls" with "-lh"` in this example matches both of the keywords:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Automatic conflict resolution
diff --git a/doc/releasenotes/rf-6.0.rst b/doc/releasenotes/rf-6.0.rst
index 8550558323f..cf00ad3e577 100644
--- a/doc/releasenotes/rf-6.0.rst
+++ b/doc/releasenotes/rf-6.0.rst
@@ -127,7 +127,7 @@ When using keywords with embedded arguments, it is pretty common that a keyword
that is used matches multiple keyword implementations. For example,
`Execute "ls" with "-lh"` in this example matches both of the keywords:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Automatic conflict resolution
diff --git a/doc/releasenotes/rf-6.0rc1.rst b/doc/releasenotes/rf-6.0rc1.rst
index 417360711a8..fca92224693 100644
--- a/doc/releasenotes/rf-6.0rc1.rst
+++ b/doc/releasenotes/rf-6.0rc1.rst
@@ -123,7 +123,7 @@ When using keywords with embedded arguments, it is pretty common that a keyword
that is used matches multiple keyword implementations. For example,
`Execute "ls" with "-lh"` in this example matches both of the keywords:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Automatic conflict resolution
diff --git a/doc/releasenotes/rf-6.0rc2.rst b/doc/releasenotes/rf-6.0rc2.rst
index 48220ab3c7b..43c8ee41259 100644
--- a/doc/releasenotes/rf-6.0rc2.rst
+++ b/doc/releasenotes/rf-6.0rc2.rst
@@ -129,7 +129,7 @@ When using keywords with embedded arguments, it is pretty common that a keyword
that is used matches multiple keyword implementations. For example,
`Execute "ls" with "-lh"` in this example matches both of the keywords:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Automatic conflict resolution
diff --git a/doc/releasenotes/rf-6.1.rst b/doc/releasenotes/rf-6.1.rst
index 4c6d37b7be8..2516947be4e 100644
--- a/doc/releasenotes/rf-6.1.rst
+++ b/doc/releasenotes/rf-6.1.rst
@@ -72,7 +72,7 @@ functionalities are explained below:
it also accepts a path or an open file where to write JSON data along with
configuration options related to JSON formatting:
- .. sourcecode:: python
+ .. code:: python
from robot.running import TestSuite
@@ -91,7 +91,7 @@ functionalities are explained below:
2. You can create a suite based on JSON data using `TestSuite.from_json`__.
It works both with JSON strings and paths to JSON files:
- .. sourcecode:: python
+ .. code:: python
from robot.running import TestSuite
@@ -117,7 +117,7 @@ that machine. To avoid such problems, it is possible to use the new
before getting the data and add a correct root directory after the suite is
recreated:
-.. sourcecode:: python
+.. code:: python
from robot.running import TestSuite
@@ -164,7 +164,7 @@ need is an `EXTENSION` or `extension` attribute that specifies the extension
or extensions they support, and a `parse` method that gets the path of the
source file to parse as an argument:
-.. sourcecode:: python
+.. code:: python
from robot.api import TestSuite
@@ -187,7 +187,7 @@ preserve state and allows passing arguments from the command like. The following
example illustrates that and, unlike the previous example, actually processes the
source file:
-.. sourcecode:: python
+.. code:: python
from pathlib import Path
from robot.api import TestSuite
@@ -221,7 +221,7 @@ __ https://robot-framework.readthedocs.io/en/latest/autodoc/robot.api.html#robot
__ https://robot-framework.readthedocs.io/en/latest/autodoc/robot.running.builder.html#robot.running.builder.settings.TestDefaults
-.. sourcecode:: python
+.. code:: python
from pathlib import Path
from robot.api import TestSuite
@@ -257,7 +257,7 @@ supports headers in format `=== Test Cases ===` in addition to
`TestSuite.from_string`__, `TestSuite.from_model`__ or
`TestSuite.from_file_system`__ factory methods for constructing the returned suite.
-.. sourcecode:: python
+.. code:: python
from pathlib import Path
from robot.running import TestDefaults, TestSuite
@@ -293,7 +293,7 @@ User keywords with both embedded and normal arguments
User keywords can nowadays mix embedded arguments and normal arguments (`#4234`_).
For example, this kind of usage is possible:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -326,7 +326,7 @@ WHILE and FOR loops, the log file can get hard to understand with many different
nesting levels. Such nested structures also increase the size of the output.xml
file. For example, even a simple keyword like:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
Example
@@ -335,7 +335,7 @@ file. For example, even a simple keyword like:
creates this much content in output.xml:
-.. sourcecode:: xml
+.. code:: xml
@@ -362,7 +362,7 @@ huge amounts of memory. When `--flattenkeywords` is used with Rebot, it is
possible to create a new flattened output.xml. For example, the above structure
is converted into this if the `Example` keyword is flattened using `--flattenkeywords`:
-.. sourcecode:: xml
+.. code:: xml
_*Content flattened.*_
@@ -376,7 +376,7 @@ during execution and without using command line options. The only thing needed
is using the new keyword tag `robot:flatten` (`#4584`_) and flattening is done
automatically. For example, if the earlier `Keyword` is changed to:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
Example
@@ -386,7 +386,7 @@ automatically. For example, if the earlier `Keyword` is changed to:
the result in output.xml will be this:
-.. sourcecode:: xml
+.. code:: xml
robot:flatten
@@ -425,7 +425,7 @@ based on the library state (`#4510`_). This can be done simply by creating
a converter that accepts two values. The first value is the value used in
the data, exactly as earlier, and the second is the library instance or module:
-.. sourcecode:: python
+.. code:: python
def converter(value, library):
...
@@ -442,7 +442,7 @@ It has been possible to create variable files using YAML in addition to Python
for long time, and nowadays also JSON variable files are supported (`#4532`_).
For example, a JSON file containing:
-.. sourcecode:: json
+.. code:: json
{
"STRING": "Hello, world!",
@@ -451,7 +451,7 @@ For example, a JSON file containing:
could be used like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Settings ***
Variables example.json
@@ -473,7 +473,7 @@ Robot Framework's WHILE__ loop has been enhanced in several different ways:
the option to `PASS` changes that. For example, the following loop runs ten
times and continues execution afterwards:
- .. sourcecode:: robotframework
+ .. code:: robotframework
*** Test Cases ***
WHILE with 'limit' and 'on_limit'
@@ -504,7 +504,7 @@ For example, the following loop is executed only twice:
__ http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#for-in-zip-loop
__ https://docs.python.org/3/library/functions.html#zip
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Variables ***
@{ANIMALS} dog cat horse cow elephant
@@ -523,7 +523,7 @@ the expected values. The example succeeds if `Get something` returns ten items
if three first ones match. What's even worse, it succeeds also if `Get something`
returns nothing.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -564,7 +564,7 @@ values (`#4682`_):
All these modes are illustrated by the following examples:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Variables ***
@{CHARACTERS} a b c d f
@@ -642,7 +642,7 @@ It is nowadays possible to use asynchronous functions (created using
the following async functions could be used as keyword `Gather Something` and
`Async Sleep`:
-.. sourcecode:: python
+.. code:: python
from asyncio import gather, sleep
@@ -781,7 +781,7 @@ Earlier if a type was not recognized at all, the used value was returned
as-is without trying conversion with the remaining types. For example, if
a keyword like:
-.. sourcecode:: python
+.. code:: python
def example(arg: Union[UnknownType, int]):
...
diff --git a/doc/releasenotes/rf-6.1a1.rst b/doc/releasenotes/rf-6.1a1.rst
index 679e00bb00d..8a1aa96e648 100644
--- a/doc/releasenotes/rf-6.1a1.rst
+++ b/doc/releasenotes/rf-6.1a1.rst
@@ -78,7 +78,7 @@ is not finalized yet, but the following things already work:
it also accepts a path or an open file where to write JSON data along with
configuration options related to JSON formatting:
- .. sourcecode:: python
+ .. code:: python
from robot.api import TestSuite
@@ -88,7 +88,7 @@ is not finalized yet, but the following things already work:
2. You can create a suite based on JSON data using `TestSuite.from_json`__.
It works both with JSON strings and paths to JSON files:
- .. sourcecode:: python
+ .. code:: python
from robot.api import TestSuite
@@ -113,7 +113,7 @@ User keywords with both embedded and normal arguments
User keywords can nowadays mix embedded arguments and normal arguments (`#4234`_).
For example, this kind of usage is possible:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -136,7 +136,7 @@ WHILE and FOR loops, the log file can get hard do understand with many different
nesting levels. Such nested structures also increase the size of the output.xml
file. For example, even a simple keyword like:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
Keyword
@@ -145,7 +145,7 @@ file. For example, even a simple keyword like:
creates this much content in output.xml:
-.. sourcecode:: xml
+.. code:: xml
@@ -172,7 +172,7 @@ huge amounts of memory. When `--flattenkeywords` is used with Rebot, it is
possible to create a new flattened output.xml. For example, the above structure
is converted into this if `Keyword` is flattened using `--flattenkeywords`:
-.. sourcecode:: xml
+.. code:: xml
_*Content flattened.*_
@@ -187,7 +187,7 @@ is using the new keyword tag `robot:flatten` (`#4584`_) and Robot handles
flattening automatically. For example, if the earlier `Keyword` is changed
to:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
Keyword
@@ -197,7 +197,7 @@ to:
the result in output.xml will be this:
-.. sourcecode:: xml
+.. code:: xml
robot:flatten
@@ -223,7 +223,7 @@ based on the library state (`#4510`_). This can be done simply by creating
a converter that accepts two values. The first value is the value used in
the data, exactly as earlier, and the second is the library instance or module:
-.. sourcecode:: python
+.. code:: python
def converter(value, library):
...
@@ -240,7 +240,7 @@ It has been possible to create variable files using YAML in addition to Python
for long time, and nowadays also JSON variable files are supported (`#4532`_).
For example, a JSON file containing:
-.. sourcecode:: json
+.. code:: json
{
"STRING": "Hello, world!",
@@ -249,7 +249,7 @@ For example, a JSON file containing:
could be used like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Settings ***
Variables example.json
@@ -296,7 +296,7 @@ Robot Framework's `FOR IN ZIP` loop behaves like Python's zip__ function so
that if lists lengths are not the same, items from longer ones are ignored.
For example, the following loop would be executed only twice:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Variables ***
@{ANIMALS} dog cat horse cow elephant
@@ -315,7 +315,7 @@ the expected values. The example succeeds if `Get something` returns ten items
if three first ones match. What's even worse, it succeeds also if `Get something`
returns nothing.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -353,7 +353,7 @@ values (`#4682`_):
All these modes are illustrated by the following examples:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Variables ***
@{CHARACTERS} a b c d f
@@ -484,7 +484,7 @@ Earlier if a type was not recognized at all, the used value was returned
as-is without trying conversion with the remaining types. For example, if
a keyword like:
-.. sourcecode:: python
+.. code:: python
def example(arg: Union[UnknownType, int]):
...
diff --git a/doc/releasenotes/rf-6.1b1.rst b/doc/releasenotes/rf-6.1b1.rst
index db27143d391..29313ed60b4 100644
--- a/doc/releasenotes/rf-6.1b1.rst
+++ b/doc/releasenotes/rf-6.1b1.rst
@@ -78,7 +78,7 @@ is not finalized yet, but the following things already work:
it also accepts a path or an open file where to write JSON data along with
configuration options related to JSON formatting:
- .. sourcecode:: python
+ .. code:: python
from robot.api import TestSuite
@@ -88,7 +88,7 @@ is not finalized yet, but the following things already work:
2. You can create a suite based on JSON data using `TestSuite.from_json`__.
It works both with JSON strings and paths to JSON files:
- .. sourcecode:: python
+ .. code:: python
from robot.api import TestSuite
@@ -127,7 +127,7 @@ need is an `EXTENSION` or `extension` attribute that specifies the extension
or extensions they support, and a `parse` method that gets the path of the
source file to parse as an argument:
-.. sourcecode:: python
+.. code:: python
from robot.api import TestSuite
@@ -150,7 +150,7 @@ preserve state and allows passing arguments from the command like. The following
example illustrates that and, unlike the previous example, actually processes the
source file:
-.. sourcecode:: python
+.. code:: python
from pathlib import Path
from robot.api import TestSuite
@@ -184,7 +184,7 @@ __ https://robot-framework.readthedocs.io/en/master/autodoc/robot.api.html#robot
__ https://robot-framework.readthedocs.io/en/master/autodoc/robot.running.builder.html#robot.running.builder.settings.TestDefaults
-.. sourcecode:: python
+.. code:: python
from pathlib import Path
from robot.api import TestSuite
@@ -220,7 +220,7 @@ supports headers in format `=== Test Cases ===` in addition to
`TestSuite.from_string`__, `TestSuite.from_model`__ or
`TestSuite.from_file_system`__ factory methods for constructing the returned suite.
-.. sourcecode:: python
+.. code:: python
from pathlib import Path
from robot.running import TestDefaults, TestSuite
@@ -245,7 +245,7 @@ User keywords with both embedded and normal arguments
User keywords can nowadays mix embedded arguments and normal arguments (`#4234`_).
For example, this kind of usage is possible:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -268,7 +268,7 @@ WHILE and FOR loops, the log file can get hard to understand with many different
nesting levels. Such nested structures also increase the size of the output.xml
file. For example, even a simple keyword like:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
Example
@@ -277,7 +277,7 @@ file. For example, even a simple keyword like:
creates this much content in output.xml:
-.. sourcecode:: xml
+.. code:: xml
@@ -304,7 +304,7 @@ huge amounts of memory. When `--flattenkeywords` is used with Rebot, it is
possible to create a new flattened output.xml. For example, the above structure
is converted into this if the `Example` keyword is flattened using `--flattenkeywords`:
-.. sourcecode:: xml
+.. code:: xml
_*Content flattened.*_
@@ -318,7 +318,7 @@ during execution and without using command line options. The only thing needed
is using the new keyword tag `robot:flatten` (`#4584`_) and flattening is done
automatically. For example, if the earlier `Keyword` is changed to:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
Example
@@ -328,7 +328,7 @@ automatically. For example, if the earlier `Keyword` is changed to:
the result in output.xml will be this:
-.. sourcecode:: xml
+.. code:: xml
robot:flatten
@@ -354,7 +354,7 @@ based on the library state (`#4510`_). This can be done simply by creating
a converter that accepts two values. The first value is the value used in
the data, exactly as earlier, and the second is the library instance or module:
-.. sourcecode:: python
+.. code:: python
def converter(value, library):
...
@@ -371,7 +371,7 @@ It has been possible to create variable files using YAML in addition to Python
for long time, and nowadays also JSON variable files are supported (`#4532`_).
For example, a JSON file containing:
-.. sourcecode:: json
+.. code:: json
{
"STRING": "Hello, world!",
@@ -380,7 +380,7 @@ For example, a JSON file containing:
could be used like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Settings ***
Variables example.json
@@ -402,7 +402,7 @@ Robot Framework's WHILE__ loop has been enhanced in several different ways:
the option to `PASS` changes that. For example, the following loop runs ten
times and continues execution afterwards:
- .. sourcecode:: robotframework
+ .. code:: robotframework
*** Test Cases ***
WHILE with 'limit' and 'on_limit'
@@ -433,7 +433,7 @@ For example, the following loop is executed only twice:
__ http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#for-in-zip-loop
__ https://docs.python.org/3/library/functions.html#zip
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Variables ***
@{ANIMALS} dog cat horse cow elephant
@@ -452,7 +452,7 @@ the expected values. The example succeeds if `Get something` returns ten items
if three first ones match. What's even worse, it succeeds also if `Get something`
returns nothing.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -493,7 +493,7 @@ values (`#4682`_):
All these modes are illustrated by the following examples:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Variables ***
@{CHARACTERS} a b c d f
@@ -572,7 +572,7 @@ It is nowadays possible to run use asynchronous functions (created using
the following async functions could be used as keyword `Gather Something` and
`Async Sleep`:
-.. sourcecode:: python
+.. code:: python
from asyncio import gather, sleep
@@ -682,7 +682,7 @@ Earlier if a type was not recognized at all, the used value was returned
as-is without trying conversion with the remaining types. For example, if
a keyword like:
-.. sourcecode:: python
+.. code:: python
def example(arg: Union[UnknownType, int]):
...
diff --git a/doc/releasenotes/rf-6.1rc1.rst b/doc/releasenotes/rf-6.1rc1.rst
index 86fcd82cef0..56c8adff90c 100644
--- a/doc/releasenotes/rf-6.1rc1.rst
+++ b/doc/releasenotes/rf-6.1rc1.rst
@@ -75,7 +75,7 @@ functionalities are explained below:
it also accepts a path or an open file where to write JSON data along with
configuration options related to JSON formatting:
- .. sourcecode:: python
+ .. code:: python
from robot.running import TestSuite
@@ -92,7 +92,7 @@ functionalities are explained below:
2. You can create a suite based on JSON data using `TestSuite.from_json`__.
It works both with JSON strings and paths to JSON files:
- .. sourcecode:: python
+ .. code:: python
from robot.running import TestSuite
@@ -116,7 +116,7 @@ that machine. To avoid such problems, it is possible to use the new
before getting the data and add a correct root directory after the suite is
recreated:
-.. sourcecode:: python
+.. code:: python
from robot.running import TestSuite
@@ -163,7 +163,7 @@ need is an `EXTENSION` or `extension` attribute that specifies the extension
or extensions they support, and a `parse` method that gets the path of the
source file to parse as an argument:
-.. sourcecode:: python
+.. code:: python
from robot.api import TestSuite
@@ -186,7 +186,7 @@ preserve state and allows passing arguments from the command like. The following
example illustrates that and, unlike the previous example, actually processes the
source file:
-.. sourcecode:: python
+.. code:: python
from pathlib import Path
from robot.api import TestSuite
@@ -220,7 +220,7 @@ __ https://robot-framework.readthedocs.io/en/latest/autodoc/robot.api.html#robot
__ https://robot-framework.readthedocs.io/en/latest/autodoc/robot.running.builder.html#robot.running.builder.settings.TestDefaults
-.. sourcecode:: python
+.. code:: python
from pathlib import Path
from robot.api import TestSuite
@@ -256,7 +256,7 @@ supports headers in format `=== Test Cases ===` in addition to
`TestSuite.from_string`__, `TestSuite.from_model`__ or
`TestSuite.from_file_system`__ factory methods for constructing the returned suite.
-.. sourcecode:: python
+.. code:: python
from pathlib import Path
from robot.running import TestDefaults, TestSuite
@@ -281,7 +281,7 @@ User keywords with both embedded and normal arguments
User keywords can nowadays mix embedded arguments and normal arguments (`#4234`_).
For example, this kind of usage is possible:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -314,7 +314,7 @@ WHILE and FOR loops, the log file can get hard to understand with many different
nesting levels. Such nested structures also increase the size of the output.xml
file. For example, even a simple keyword like:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
Example
@@ -323,7 +323,7 @@ file. For example, even a simple keyword like:
creates this much content in output.xml:
-.. sourcecode:: xml
+.. code:: xml
@@ -350,7 +350,7 @@ huge amounts of memory. When `--flattenkeywords` is used with Rebot, it is
possible to create a new flattened output.xml. For example, the above structure
is converted into this if the `Example` keyword is flattened using `--flattenkeywords`:
-.. sourcecode:: xml
+.. code:: xml
_*Content flattened.*_
@@ -364,7 +364,7 @@ during execution and without using command line options. The only thing needed
is using the new keyword tag `robot:flatten` (`#4584`_) and flattening is done
automatically. For example, if the earlier `Keyword` is changed to:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Keywords ***
Example
@@ -374,7 +374,7 @@ automatically. For example, if the earlier `Keyword` is changed to:
the result in output.xml will be this:
-.. sourcecode:: xml
+.. code:: xml
robot:flatten
@@ -413,7 +413,7 @@ based on the library state (`#4510`_). This can be done simply by creating
a converter that accepts two values. The first value is the value used in
the data, exactly as earlier, and the second is the library instance or module:
-.. sourcecode:: python
+.. code:: python
def converter(value, library):
...
@@ -430,7 +430,7 @@ It has been possible to create variable files using YAML in addition to Python
for long time, and nowadays also JSON variable files are supported (`#4532`_).
For example, a JSON file containing:
-.. sourcecode:: json
+.. code:: json
{
"STRING": "Hello, world!",
@@ -439,7 +439,7 @@ For example, a JSON file containing:
could be used like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Settings ***
Variables example.json
@@ -461,7 +461,7 @@ Robot Framework's WHILE__ loop has been enhanced in several different ways:
the option to `PASS` changes that. For example, the following loop runs ten
times and continues execution afterwards:
- .. sourcecode:: robotframework
+ .. code:: robotframework
*** Test Cases ***
WHILE with 'limit' and 'on_limit'
@@ -492,7 +492,7 @@ For example, the following loop is executed only twice:
__ http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#for-in-zip-loop
__ https://docs.python.org/3/library/functions.html#zip
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Variables ***
@{ANIMALS} dog cat horse cow elephant
@@ -511,7 +511,7 @@ the expected values. The example succeeds if `Get something` returns ten items
if three first ones match. What's even worse, it succeeds also if `Get something`
returns nothing.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -552,7 +552,7 @@ values (`#4682`_):
All these modes are illustrated by the following examples:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Variables ***
@{CHARACTERS} a b c d f
@@ -631,7 +631,7 @@ It is nowadays possible to use asynchronous functions (created using
the following async functions could be used as keyword `Gather Something` and
`Async Sleep`:
-.. sourcecode:: python
+.. code:: python
from asyncio import gather, sleep
@@ -764,7 +764,7 @@ Earlier if a type was not recognized at all, the used value was returned
as-is without trying conversion with the remaining types. For example, if
a keyword like:
-.. sourcecode:: python
+.. code:: python
def example(arg: Union[UnknownType, int]):
...
diff --git a/doc/releasenotes/rf-7.0.1.rst b/doc/releasenotes/rf-7.0.1.rst
new file mode 100644
index 00000000000..4eec6f3241a
--- /dev/null
+++ b/doc/releasenotes/rf-7.0.1.rst
@@ -0,0 +1,224 @@
+=====================
+Robot Framework 7.0.1
+=====================
+
+.. default-role:: code
+
+`Robot Framework`_ 7.0.1 is the first and the only planned bug fix release in
+the Robot Framework 7.0.x series. It fixes all reported regressions as well as
+some issues affecting also earlier versions. The only bigger enhancement it
+contains is the new Japanese localization__ (`#5069`_).
+
+__ https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#localization
+
+Questions and comments related to the release can be sent to the `#devel`
+channel on `Robot Framework Slack`_ and possible bugs submitted to
+the `issue tracker`_.
+
+If you have pip_ installed, just run
+
+::
+
+ pip install --pre --upgrade robotframework
+
+to install the latest available release or use
+
+::
+
+ pip install robotframework==7.0.1
+
+to install exactly this version. Alternatively you can download the package
+from PyPI_ and install it manually. For more details and other installation
+approaches, see the `installation instructions`_.
+
+Robot Framework 7.0.1 was released on Monday June 10, 2024.
+
+.. _Robot Framework: http://robotframework.org
+.. _Robot Framework Foundation: http://robotframework.org/foundation
+.. _pip: http://pip-installer.org
+.. _PyPI: https://pypi.python.org/pypi/robotframework
+.. _issue tracker milestone: https://github.com/robotframework/robotframework/issues?q=milestone%3Av7.0.1
+.. _issue tracker: https://github.com/robotframework/robotframework/issues
+.. _robotframework-users: http://groups.google.com/group/robotframework-users
+.. _Slack: http://slack.robotframework.org
+.. _Robot Framework Slack: Slack_
+.. _installation instructions: ../../INSTALL.rst
+
+
+.. contents::
+ :depth: 2
+ :local:
+
+Backwards incompatible changes
+==============================
+
+`--test` and `--include` options are non-cumulative again
+---------------------------------------------------------
+
+Robot Framework 7.0 changed the behavior of the `--test` and `--include` options
+so that a test (or a task) is selected if it matches either of these options, when
+earlier it needed to match both of them (`#4721`__). This behavior was considered
+more useful back then, but it turned out to cause problems with the `--rerun-failed`
+option and with the Pabot__ tool. In the end the benefits of the new behavior were
+not considered important enough compared to the problems, and in Robot Framework 7.0.1
+the behavior was changed back to what it was earlier (`#5023`_).
+
+__ https://github.com/robotframework/robotframework/issues/4721
+__ https://pabot.org
+
+Changes to passing named arguments with non-string values programmatically
+--------------------------------------------------------------------------
+
+Robot Framework 7.0 attempted to provide a convenient API for creating keywords
+programmatically, and the key feature was the possibility to use named arguments
+with non-string values (`#5000`__). The selected approach unfortunately caused
+backwards incompatibility problems and needed to be reverted (`#5031`_).
+
+Because using named arguments with non-string values is important, for example,
+to the DataDriver__ tool, a new semi-private API for exactly that purpose was
+added as part of the issue `#5031`_. That API has some limitations, though, and
+the plan is to add a better public API in Robot Framework 7.1 (`#5143`__).
+
+__ https://github.com/robotframework/robotframework/issues/5000
+__ https://github.com/Snooz82/robotframework-datadriver
+__ https://github.com/robotframework/robotframework/issues/5143
+
+Acknowledgements
+================
+
+Robot Framework development is sponsored by the `Robot Framework Foundation`_
+and its over 60 member organizations. If your organization is using Robot Framework
+and benefiting from it, consider joining the foundation to support its
+development as well.
+
+The community has also done some great contributions:
+
+- `Yas `__ provided Japanese localization (`#5069`_).
+- `Lukas Rolf `__ fixed a crash occurring with the Dialogs
+ library (`#4993`_).
+- `Many Kasiriha `__ implemented a better theme for
+ the `API docs `__ (`#5039`_).
+- `Federico Pellegrin `__ fixed unit tests under
+ the forthcoming `Python 3.13`__ (`#5035`_). Official support for Python 3.13
+ is planned for Robot Framework 7.1 (`#5091`__).
+- `@sunday2 `__ fixed a problem with acceptance tests
+ when a directory path contains spaces (`#5100`_).
+
+__ https://docs.python.org/3.13/whatsnew/3.13.html
+__ https://github.com/robotframework/robotframework/issues/5091
+
+Big thanks to Robot Framework Foundation, to the community members listed above,
+and to everyone else who has submitted bug reports, debugged problems, or otherwise
+helped with Robot Framework 7.0.1 development.
+
+| `Pekka Klärck `_
+| Robot Framework lead developer
+
+Full list of fixes and enhancements
+===================================
+
+.. list-table::
+ :header-rows: 1
+
+ * - ID
+ - Type
+ - Priority
+ - Summary
+ * - `#5012`_
+ - bug
+ - high
+ - `--legacy-output` does not work with Rebot when combining, merging or filtering results
+ * - `#5023`_
+ - bug
+ - high
+ - Make `--test` and `--include` non-cumulative again due to backwards incompatibility problems
+ * - `#5031`_
+ - bug
+ - high
+ - Problems with non-string arguments when using `BuildIn.run_keyword`
+ * - `#5063`_
+ - bug
+ - high
+ - Robot Framework does not run outside Windows if `signal.setitimer` is not available (affects e.g. Pyodide)
+ * - `#5120`_
+ - bug
+ - high
+ - Incorrect syntax in for loop crashes RF and html reports are not generated.
+ * - `#5069`_
+ - enhancement
+ - high
+ - Japanese localization
+ * - `#4993`_
+ - bug
+ - medium
+ - Dialogs: Problems closing dialogs on Linux
+ * - `#5017`_
+ - bug
+ - medium
+ - `start/end_body_item` listener v3 methods missing from documentation in User Guide
+ * - `#5051`_
+ - bug
+ - medium
+ - Logging unexecuted keyword has unnecessary overhead if keywords are not found
+ * - `#5114`_
+ - bug
+ - medium
+ - Dictionaries are not accepted as-is in argument conversion with unions containing TypedDicts
+ * - `#5115`_
+ - bug
+ - medium
+ - `NotRequired` and `Required` not properly handled with Python < 3.11 in `TypedDict` conversion
+ * - `#5128`_
+ - bug
+ - medium
+ - Named-only arguments are not trace logged with user keywords
+ * - `#5039`_
+ - enhancement
+ - medium
+ - Use better theme with API docs at Read The Docs
+ * - `#5134`_
+ - enhancement
+ - medium
+ - Add new section about style to User Guide
+ * - `#5032`_
+ - bug
+ - low
+ - Collections: No default value shown in documentation for `Get/Pop From Dictionary`
+ * - `#5035`_
+ - bug
+ - low
+ - Unit test fails under Python 3.13
+ * - `#5070`_
+ - bug
+ - low
+ - Typo in listener interface documentation in the User Guide
+ * - `#5100`_
+ - bug
+ - low
+ - Acceptance tests fail if directory path contains spaces
+ * - `#5130`_
+ - bug
+ - low
+ - Collections: Value of `ignore_case` argument accidentally logged
+
+Altogether 19 issues. View on the `issue tracker `__.
+
+.. _#5012: https://github.com/robotframework/robotframework/issues/5012
+.. _#5023: https://github.com/robotframework/robotframework/issues/5023
+.. _#5031: https://github.com/robotframework/robotframework/issues/5031
+.. _#5063: https://github.com/robotframework/robotframework/issues/5063
+.. _#5120: https://github.com/robotframework/robotframework/issues/5120
+.. _#5069: https://github.com/robotframework/robotframework/issues/5069
+.. _#4993: https://github.com/robotframework/robotframework/issues/4993
+.. _#5017: https://github.com/robotframework/robotframework/issues/5017
+.. _#5051: https://github.com/robotframework/robotframework/issues/5051
+.. _#5114: https://github.com/robotframework/robotframework/issues/5114
+.. _#5115: https://github.com/robotframework/robotframework/issues/5115
+.. _#5128: https://github.com/robotframework/robotframework/issues/5128
+.. _#5039: https://github.com/robotframework/robotframework/issues/5039
+.. _#5134: https://github.com/robotframework/robotframework/issues/5134
+.. _#5032: https://github.com/robotframework/robotframework/issues/5032
+.. _#5035: https://github.com/robotframework/robotframework/issues/5035
+.. _#5070: https://github.com/robotframework/robotframework/issues/5070
+.. _#5100: https://github.com/robotframework/robotframework/issues/5100
+.. _#5130: https://github.com/robotframework/robotframework/issues/5130
diff --git a/doc/releasenotes/rf-7.0.1rc1.rst b/doc/releasenotes/rf-7.0.1rc1.rst
new file mode 100644
index 00000000000..a08159a07c9
--- /dev/null
+++ b/doc/releasenotes/rf-7.0.1rc1.rst
@@ -0,0 +1,232 @@
+=========================================
+Robot Framework 7.0.1 release candidate 1
+=========================================
+
+.. default-role:: code
+
+`Robot Framework`_ 7.0.1 is the first and the only planned bug fix release in
+the Robot Framework 7.0.x series. It fixes all reported regressions as well as
+some issues affecting also earlier versions. The only bigger enhancement it
+contains is the new Japanese localization__ (`#5069`_).
+
+__ https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#localization
+
+Questions and comments related to the release can be sent to the `#devel`
+channel on `Robot Framework Slack`_ and possible bugs submitted to
+the `issue tracker`_.
+
+If you have pip_ installed, just run
+
+::
+
+ pip install --pre --upgrade robotframework
+
+to install the latest available release or use
+
+::
+
+ pip install robotframework==7.0.1rc1
+
+to install exactly this version. Alternatively you can download the package
+from PyPI_ and install it manually. For more details and other installation
+approaches, see the `installation instructions`_.
+
+Robot Framework 7.0.1 release candidate 1 was released on Tuesday June 4, 2024.
+It is especially targeted for users who have reported issues so that they can
+verify that fixes work properly. The final Robot Framework 7.0 version is
+planned to be released on Monday June 10, 2024.
+
+.. _Robot Framework: http://robotframework.org
+.. _Robot Framework Foundation: http://robotframework.org/foundation
+.. _pip: http://pip-installer.org
+.. _PyPI: https://pypi.python.org/pypi/robotframework
+.. _issue tracker milestone: https://github.com/robotframework/robotframework/issues?q=milestone%3Av7.0.1
+.. _issue tracker: https://github.com/robotframework/robotframework/issues
+.. _robotframework-users: http://groups.google.com/group/robotframework-users
+.. _Slack: http://slack.robotframework.org
+.. _Robot Framework Slack: Slack_
+.. _installation instructions: ../../INSTALL.rst
+
+.. contents::
+ :depth: 2
+ :local:
+
+Backwards incompatible changes
+==============================
+
+`--test` and `--include` options are non-cumulative again
+---------------------------------------------------------
+
+Robot Framework 7.0 changed the behavior of the `--test` and `--include` options
+so that a test (or a task) is selected if it matches either of these options when
+earlier it needed to match both of them (`#4721`__). This behavior was considered
+more useful back then, but it turned out to cause problems with the `--rerun-failed`
+option and with the Pabot tool. In the end the benefits of the new behavior were
+not considered important enough compared to the problems, and the behavior was
+changed back to what it was earlier (`#5023`_).
+
+__ https://github.com/robotframework/robotframework/issues/4721
+
+Not possible anymore to pass named arguments as non-strings programmatically
+----------------------------------------------------------------------------
+
+Robot Framework 7.0 attempted to provide a nicer API for executing keywords
+programmatically so that non-string values could be used as named arguments (`#5000`__).
+The selected approach unfortunately caused problems in some programmatic usages
+and it was reverted (`#5031`_).
+
+__ https://github.com/robotframework/robotframework/issues/5000
+
+Acknowledgements
+================
+
+Robot Framework development is sponsored by the `Robot Framework Foundation`_
+and its over 60 member organizations. If your organization is using Robot Framework
+and benefiting from it, consider joining the foundation to support its
+development as well.
+
+The community has also done some great contributions:
+
+- `Yas `__ provided Japanese localization (`#5069`_).
+- `Lukas Rolf `__ fixed a crash occurring with the Dialogs
+ library (`#4993`_).
+- `Many Kasiriha `__ implemented a better theme for
+ the `API docs `__ (`#5039`_).
+- `Federico Pellegrin `__ fixed unit tests under
+ the forthcoming `Python 3.13`__ (`#5035`_). Official support for Python 3.13
+ is planned for `Robot Framework 7.1`__.
+- `@sunday2 `__ fixed a problem with acceptance tests
+ when a directory path contains spaces (`#5100`_).
+
+__ https://docs.python.org/3.13/whatsnew/3.13.html
+__ https://github.com/robotframework/robotframework/issues/5091
+
+Big thanks to Robot Framework Foundation, to the community members listed above,
+and to everyone else who has submitted bug reports, debugged problems, or otherwise
+helped with Robot Framework 7.0.1 development.
+
+| `Pekka Klärck `_
+| Robot Framework lead developer
+
+Full list of fixes and enhancements
+===================================
+
+.. list-table::
+ :header-rows: 1
+
+ * - ID
+ - Type
+ - Priority
+ - Summary
+ - Added
+ * - `#5012`_
+ - bug
+ - high
+ - `--legacy-output` does not work with Rebot when combining, merging or filtering results
+ - rc 1
+ * - `#5023`_
+ - bug
+ - high
+ - Make `--test` and `--include` non-cumulative again due to backwards incompatibility problems
+ - rc 1
+ * - `#5031`_
+ - bug
+ - high
+ - Problems with non-string arguments when using `BuildIn.run_keyword`
+ - rc 1
+ * - `#5063`_
+ - bug
+ - high
+ - Robot Framework does not run outside Windows if `signal.setitimer` is not available (affects e.g. Pyodide)
+ - rc 1
+ * - `#5069`_
+ - enhancement
+ - high
+ - Japanese localization
+ - rc 1
+ * - `#5120`_
+ - bug
+ - high
+ - Incorrect syntax in for loop crashes RF and html reports are not generated.
+ - rc 1
+ * - `#4993`_
+ - bug
+ - medium
+ - Dialogs: Problems closing dialogs on Linux
+ - rc 1
+ * - `#5017`_
+ - bug
+ - medium
+ - `start/end_body_item` listener v3 methods missing from documentation in User Guide
+ - rc 1
+ * - `#5051`_
+ - bug
+ - medium
+ - Logging unexecuted keyword has unnecessary overhead if keywords are not found
+ - rc 1
+ * - `#5114`_
+ - bug
+ - medium
+ - Dictionaries are not accepted as-is in argument conversion with unions containing TypedDicts
+ - rc 1
+ * - `#5115`_
+ - bug
+ - medium
+ - `NotRequired` and `Required` not properly handled with Python < 3.11 in `TypedDict` conversion
+ - rc 1
+ * - `#5128`_
+ - bug
+ - medium
+ - Named-only arguments are not trace logged with user keywords
+ - rc 1
+ * - `#5039`_
+ - enhancement
+ - medium
+ - Use better theme with API docs at Read The Docs
+ - rc 1
+ * - `#5032`_
+ - bug
+ - low
+ - Collections: No default value shown in documentation for `Get/Pop From Dictionary`
+ - rc 1
+ * - `#5035`_
+ - bug
+ - low
+ - Unit test fails under Python 3.13
+ - rc 1
+ * - `#5070`_
+ - bug
+ - low
+ - Typo in listener interface documentation in the User Guide
+ - rc 1
+ * - `#5100`_
+ - bug
+ - low
+ - Acceptance tests fail if directory path contains spaces
+ - rc 1
+ * - `#5130`_
+ - bug
+ - low
+ - Collections: Value of `ignore_case` argument accidentally logged
+ - rc 1
+
+Altogether 18 issues. View on the `issue tracker `__.
+
+.. _#5012: https://github.com/robotframework/robotframework/issues/5012
+.. _#5023: https://github.com/robotframework/robotframework/issues/5023
+.. _#5031: https://github.com/robotframework/robotframework/issues/5031
+.. _#5063: https://github.com/robotframework/robotframework/issues/5063
+.. _#5120: https://github.com/robotframework/robotframework/issues/5120
+.. _#4993: https://github.com/robotframework/robotframework/issues/4993
+.. _#5017: https://github.com/robotframework/robotframework/issues/5017
+.. _#5051: https://github.com/robotframework/robotframework/issues/5051
+.. _#5114: https://github.com/robotframework/robotframework/issues/5114
+.. _#5115: https://github.com/robotframework/robotframework/issues/5115
+.. _#5128: https://github.com/robotframework/robotframework/issues/5128
+.. _#5039: https://github.com/robotframework/robotframework/issues/5039
+.. _#5069: https://github.com/robotframework/robotframework/issues/5069
+.. _#5032: https://github.com/robotframework/robotframework/issues/5032
+.. _#5035: https://github.com/robotframework/robotframework/issues/5035
+.. _#5070: https://github.com/robotframework/robotframework/issues/5070
+.. _#5100: https://github.com/robotframework/robotframework/issues/5100
+.. _#5130: https://github.com/robotframework/robotframework/issues/5130
diff --git a/doc/releasenotes/rf-7.0.1rc2.rst b/doc/releasenotes/rf-7.0.1rc2.rst
new file mode 100644
index 00000000000..6b2b82750fb
--- /dev/null
+++ b/doc/releasenotes/rf-7.0.1rc2.rst
@@ -0,0 +1,239 @@
+=========================================
+Robot Framework 7.0.1 release candidate 2
+=========================================
+
+.. default-role:: code
+
+`Robot Framework`_ 7.0.1 is the first and the only planned bug fix release in
+the Robot Framework 7.0.x series. It fixes all reported regressions as well as
+some issues affecting also earlier versions. The only bigger enhancement it
+contains is the new Japanese localization__ (`#5069`_).
+
+__ https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#localization
+
+Questions and comments related to the release can be sent to the `#devel`
+channel on `Robot Framework Slack`_ and possible bugs submitted to
+the `issue tracker`_.
+
+If you have pip_ installed, just run
+
+::
+
+ pip install --pre --upgrade robotframework
+
+to install the latest available release or use
+
+::
+
+ pip install robotframework==7.0.1rc2
+
+to install exactly this version. Alternatively you can download the package
+from PyPI_ and install it manually. For more details and other installation
+approaches, see the `installation instructions`_.
+
+Robot Framework 7.0.1 release candidate 2 was released on Friday June 7, 2024.
+It is especially targeted for users who have reported issues so that they can
+verify that fixes work properly. The final Robot Framework 7.0 version is
+planned to be released on Monday June 10, 2024.
+
+.. _Robot Framework: http://robotframework.org
+.. _Robot Framework Foundation: http://robotframework.org/foundation
+.. _pip: http://pip-installer.org
+.. _PyPI: https://pypi.python.org/pypi/robotframework
+.. _issue tracker: https://github.com/robotframework/robotframework/issues
+.. _robotframework-users: http://groups.google.com/group/robotframework-users
+.. _Slack: http://slack.robotframework.org
+.. _Robot Framework Slack: Slack_
+.. _installation instructions: ../../INSTALL.rst
+
+.. contents::
+ :depth: 2
+ :local:
+
+Backwards incompatible changes
+==============================
+
+`--test` and `--include` options are non-cumulative again
+---------------------------------------------------------
+
+Robot Framework 7.0 changed the behavior of the `--test` and `--include` options
+so that a test (or a task) is selected if it matches either of these options when
+earlier it needed to match both of them (`#4721`__). This behavior was considered
+more useful back then, but it turned out to cause problems with the `--rerun-failed`
+option and with the Pabot__ tool. In the end the benefits of the new behavior were
+not considered important enough compared to the problems, and the behavior was
+changed back to what it was earlier in Robot Framework 7.0.1 (`#5023`_).
+
+__ https://github.com/robotframework/robotframework/issues/4721
+__ https://pabot.org/
+
+Changes to passing named arguments with non-string values programmatically
+--------------------------------------------------------------------------
+
+Robot Framework 7.0 attempted to provide a convenient API for creating keywords
+programmatically, and the key feature was the possibility to use named arguments
+with non-string values (`#5000`__). The selected approach unfortunately caused
+backwards incompatibility problems and needed to be reverted (`#5031`_).
+
+Because using named arguments with non-string values is important, for example,
+to the DataDriver__ tool, a new semi-private API for exactly that purpose was
+added as part of the issue `#5031`_. That API has some limitations, though, and
+the plan is to add a better public API in Robot Framework 7.1 (`#5143`__).
+
+__ https://github.com/robotframework/robotframework/issues/5000
+__ https://github.com/Snooz82/robotframework-datadriver
+__ https://github.com/robotframework/robotframework/issues/5143
+
+Acknowledgements
+================
+
+Robot Framework development is sponsored by the `Robot Framework Foundation`_
+and its over 60 member organizations. If your organization is using Robot Framework
+and benefiting from it, consider joining the foundation to support its
+development as well.
+
+The community has also done some great contributions:
+
+- `Yas `__ provided Japanese localization (`#5069`_).
+- `Lukas Rolf `__ fixed a crash occurring with the Dialogs
+ library (`#4993`_).
+- `Many Kasiriha `__ implemented a better theme for
+ the `API docs `__ (`#5039`_).
+- `Federico Pellegrin `__ fixed unit tests under
+ the forthcoming `Python 3.13`__ (`#5035`_). Official support for Python 3.13
+ is planned for Robot Framework 7.1 (`#5091`__).
+- `@sunday2 `__ fixed a problem with acceptance tests
+ when a directory path contains spaces (`#5100`_).
+
+__ https://docs.python.org/3.13/whatsnew/3.13.html
+__ https://github.com/robotframework/robotframework/issues/5091
+
+Big thanks to Robot Framework Foundation, to the community members listed above,
+and to everyone else who has submitted bug reports, debugged problems, or otherwise
+helped with Robot Framework 7.0.1 development.
+
+| `Pekka Klärck `_
+| Robot Framework lead developer
+
+Full list of fixes and enhancements
+===================================
+
+.. list-table::
+ :header-rows: 1
+
+ * - ID
+ - Type
+ - Priority
+ - Summary
+ - Added
+ * - `#5012`_
+ - bug
+ - high
+ - `--legacy-output` does not work with Rebot when combining, merging or filtering results
+ - rc 1
+ * - `#5023`_
+ - bug
+ - high
+ - Make `--test` and `--include` non-cumulative again due to backwards incompatibility problems
+ - rc 1
+ * - `#5031`_
+ - bug
+ - high
+ - Problems with non-string arguments when using `BuildIn.run_keyword`
+ - rc 2
+ * - `#5063`_
+ - bug
+ - high
+ - Robot Framework does not run outside Windows if `signal.setitimer` is not available (affects e.g. Pyodide)
+ - rc 1
+ * - `#5069`_
+ - enhancement
+ - high
+ - Japanese localization
+ - rc 1
+ * - `#5120`_
+ - bug
+ - high
+ - Incorrect syntax in for loop crashes RF and html reports are not generated.
+ - rc 1
+ * - `#4993`_
+ - bug
+ - medium
+ - Dialogs: Problems closing dialogs on Linux
+ - rc 1
+ * - `#5017`_
+ - bug
+ - medium
+ - `start/end_body_item` listener v3 methods missing from documentation in User Guide
+ - rc 1
+ * - `#5051`_
+ - bug
+ - medium
+ - Logging unexecuted keyword has unnecessary overhead if keywords are not found
+ - rc 1
+ * - `#5114`_
+ - bug
+ - medium
+ - Dictionaries are not accepted as-is in argument conversion with unions containing TypedDicts
+ - rc 1
+ * - `#5115`_
+ - bug
+ - medium
+ - `NotRequired` and `Required` not properly handled with Python < 3.11 in `TypedDict` conversion
+ - rc 1
+ * - `#5128`_
+ - bug
+ - medium
+ - Named-only arguments are not trace logged with user keywords
+ - rc 1
+ * - `#5039`_
+ - enhancement
+ - medium
+ - Use better theme with API docs at Read The Docs
+ - rc 1
+ * - `#5032`_
+ - bug
+ - low
+ - Collections: No default value shown in documentation for `Get/Pop From Dictionary`
+ - rc 1
+ * - `#5035`_
+ - bug
+ - low
+ - Unit test fails under Python 3.13
+ - rc 1
+ * - `#5070`_
+ - bug
+ - low
+ - Typo in listener interface documentation in the User Guide
+ - rc 1
+ * - `#5100`_
+ - bug
+ - low
+ - Acceptance tests fail if directory path contains spaces
+ - rc 1
+ * - `#5130`_
+ - bug
+ - low
+ - Collections: Value of `ignore_case` argument accidentally logged
+ - rc 1
+
+Altogether 18 issues. View on the `issue tracker `__.
+
+.. _#5012: https://github.com/robotframework/robotframework/issues/5012
+.. _#5023: https://github.com/robotframework/robotframework/issues/5023
+.. _#5031: https://github.com/robotframework/robotframework/issues/5031
+.. _#5063: https://github.com/robotframework/robotframework/issues/5063
+.. _#5120: https://github.com/robotframework/robotframework/issues/5120
+.. _#4993: https://github.com/robotframework/robotframework/issues/4993
+.. _#5017: https://github.com/robotframework/robotframework/issues/5017
+.. _#5051: https://github.com/robotframework/robotframework/issues/5051
+.. _#5114: https://github.com/robotframework/robotframework/issues/5114
+.. _#5115: https://github.com/robotframework/robotframework/issues/5115
+.. _#5128: https://github.com/robotframework/robotframework/issues/5128
+.. _#5039: https://github.com/robotframework/robotframework/issues/5039
+.. _#5069: https://github.com/robotframework/robotframework/issues/5069
+.. _#5032: https://github.com/robotframework/robotframework/issues/5032
+.. _#5035: https://github.com/robotframework/robotframework/issues/5035
+.. _#5070: https://github.com/robotframework/robotframework/issues/5070
+.. _#5100: https://github.com/robotframework/robotframework/issues/5100
+.. _#5130: https://github.com/robotframework/robotframework/issues/5130
diff --git a/doc/releasenotes/rf-7.0.pdf b/doc/releasenotes/rf-7.0.pdf
deleted file mode 100644
index b8a2e4d5b0b..00000000000
Binary files a/doc/releasenotes/rf-7.0.pdf and /dev/null differ
diff --git a/doc/releasenotes/rf-7.0.rst b/doc/releasenotes/rf-7.0.rst
index a9e0e1b448f..aae74945c60 100644
--- a/doc/releasenotes/rf-7.0.rst
+++ b/doc/releasenotes/rf-7.0.rst
@@ -2,12 +2,6 @@
Robot Framework 7.0
===================
-
-..
- GitHub fails to render this file at the moment.
- View rf-7.0.pdf until the problem is resolved.
-
-
.. default-role:: code
`Robot Framework`_ 7.0 is a new major release with highly enhanced listener interface
@@ -95,7 +89,7 @@ version 3 has been extended to support also keywords and control structures (`#3
For example, a listener having the following methods prints information
about started keywords and ended WHILE loops:
-.. sourcecode:: python
+.. code:: python
from robot import result, running
@@ -112,7 +106,7 @@ With keyword calls it is possible to also get more information about the actuall
executed keyword. For example, the following listener prints some information
about the executed keyword and the library it belongs to:
-.. sourcecode:: python
+.. code:: python
from robot.running import Keyword as KeywordData, LibraryKeyword
from robot.result import Keyword as KeywordResult
@@ -173,7 +167,7 @@ in the `__init__` method. Robot Framework 7.0 makes it possible to use string
that a listener can be specified as a class attribute and not only in `__init__`.
This is especially convenient when using the `@library` decorator:
-.. sourcecode:: python
+.. code:: python
from robot.api.deco import keyword, library
@@ -216,7 +210,7 @@ the syntax for creating variables in different scopes. Except for the mandatory
`VAR` marker, the syntax is also the same as when creating variables in the
Variables section. The syntax is best explained with examples:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -242,7 +236,7 @@ in the Variables section, it is possible to create also `@{list}` and `&{dict}`
variables. Unlike in the Variables section, variables can be created conditionally
using IF/ELSE structures:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Long value
@@ -286,7 +280,7 @@ accepts more arguments than there are embedded arguments, the remaining argument
can be passed in as normal arguments. This is illustrated by the following example
keyword:
-.. sourcecode:: python
+.. code:: python
@keyword('Number of ${animals} should be')
def example(animals, count):
@@ -294,7 +288,7 @@ keyword:
The above keyword could be used like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -353,7 +347,7 @@ In Python, the Literal__ type makes it possible to type arguments so that type
checkers accept only certain values. For example, this function only accepts
strings `x`, `y` and `z`:
-.. sourcecode:: python
+.. code:: python
def example(arg: Literal['x', 'y', 'z']):
...
@@ -392,7 +386,7 @@ Support for stringified generics and unions has now been added also to
Robot Framework's argument conversion (`#4711`_). For example,
the following typing now also works with Python 3.8:
-.. sourcecode:: python
+.. code:: python
def example(a: 'list[int]', b: 'int | float'):
...
@@ -412,7 +406,7 @@ the `-tag` syntax with their own `[Tags]` setting (`#4374`_). For example,
tests `T1` and `T3` below get tags `all` and `most`, and test `T2` gets
tags `all` and `one`:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Settings ***
Test Tags all most
@@ -536,7 +530,7 @@ are now stored to `start` and `elapsed` attributes and message times to `time`.
Examples:
-.. sourcecode:: xml
+.. code:: xml
Hello world!
@@ -568,7 +562,7 @@ the result model.
Examples:
-.. sourcecode:: xml
+.. code:: xml
...
diff --git a/doc/releasenotes/rf-7.0a1.rst b/doc/releasenotes/rf-7.0a1.rst
index ffa0710255d..cafe2ca6617 100644
--- a/doc/releasenotes/rf-7.0a1.rst
+++ b/doc/releasenotes/rf-7.0a1.rst
@@ -73,7 +73,7 @@ the syntax for creating variables in different scopes. Except for the mandatory
`VAR` marker, the syntax is also the same as when creating variables in the
Variables section. The syntax is best explained with examples:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -99,7 +99,7 @@ in the Variables section, it is possible to create also `@{list}` and `&{dict}`
variables. Unlike in the Variables section, variables can be created conditionally
using IF/ELSE structures.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Long value
@@ -143,13 +143,13 @@ accepts more arguments than there are embedded arguments, the remaining argument
can be passed in as normal arguments. For example, the keyword and its usage shown
below demonstrate this:
-.. sourcecode:: python
+.. code:: python
@keyword('Number of ${animals} should be')
def example(animals, count):
...
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -167,7 +167,7 @@ section with `Test Tags` or `Keyword Tags` settings by using the `-tag` syntax
(`#4374`_). For example, in the following test cases, `T1` and `T3` are assigned
the tags `all` and `most`, while `T2` is assigned `all` and `one`.
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Settings ***
Test Tags all most
@@ -200,7 +200,7 @@ Support for stringified generics and unions has now been added also to
Robot Framework's argument conversion (`#4711`_). For example,
the following typing now also works with Python 3.8:
-.. sourcecode:: python
+.. code:: python
def example(a: 'list[int]', b: 'int | float'):
...
@@ -290,7 +290,7 @@ times to `time`.
Examples:
-.. sourcecode:: xml
+.. code:: xml
Hello world!
@@ -323,7 +323,7 @@ the result model.
Examples:
-.. sourcecode:: xml
+.. code:: xml
...
diff --git a/doc/releasenotes/rf-7.0a2.rst b/doc/releasenotes/rf-7.0a2.rst
index d8d4a80b432..2811d36e0b4 100644
--- a/doc/releasenotes/rf-7.0a2.rst
+++ b/doc/releasenotes/rf-7.0a2.rst
@@ -74,7 +74,7 @@ the syntax for creating variables in different scopes. Except for the mandatory
`VAR` marker, the syntax is also the same as when creating variables in the
Variables section. The syntax is best explained with examples:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -100,7 +100,7 @@ in the Variables section, it is possible to create also `@{list}` and `&{dict}`
variables. Unlike in the Variables section, variables can be created conditionally
using IF/ELSE structures:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Long value
@@ -144,7 +144,7 @@ accepts more arguments than there are embedded arguments, the remaining argument
can be passed in as normal arguments. This is illustrated by the following example
keyword:
-.. sourcecode:: python
+.. code:: python
@keyword('Number of ${animals} should be')
def example(animals, count):
@@ -152,7 +152,7 @@ keyword:
The above keyword could be used like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -170,7 +170,7 @@ section with `Test Tags` or `Keyword Tags` settings by using the `-tag` syntax
(`#4374`_). For example, tests `T1` and `T3` below are given tags `all` and
`most`, and test `T2` gets tags `all` and `one`:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Settings ***
Test Tags all most
@@ -216,7 +216,7 @@ Support for stringified generics and unions has now been added also to
Robot Framework's argument conversion (`#4711`_). For example,
the following typing now also works with Python 3.8:
-.. sourcecode:: python
+.. code:: python
def example(a: 'list[int]', b: 'int | float'):
...
@@ -310,7 +310,7 @@ are now stored to `start` and `elapsed` attributes and message times to `time`.
Examples:
-.. sourcecode:: xml
+.. code:: xml
Hello world!
@@ -342,7 +342,7 @@ the result model.
Examples:
-.. sourcecode:: xml
+.. code:: xml
...
diff --git a/doc/releasenotes/rf-7.0b1.rst b/doc/releasenotes/rf-7.0b1.rst
index 27b6de1828c..9b3e7b6f88a 100644
--- a/doc/releasenotes/rf-7.0b1.rst
+++ b/doc/releasenotes/rf-7.0b1.rst
@@ -82,7 +82,7 @@ version 3 has been extended to support also keywords and control structures (`#3
For example, a listener having the following methods would print information
about started keywords and ended WHILE loops:
-.. sourcecode:: python
+.. code:: python
from robot.running import Keyword as KeywordData, While as WhileData
from robot.result import Keyword as KeywordResult, While as WhileResult
@@ -101,7 +101,7 @@ With keywords it is possible to also get more information about the actually
executed keyword. For example, the following listener prints some information
about the executed keyword and the library it belongs to:
-.. sourcecode:: python
+.. code:: python
from robot.running import Keyword as KeywordData, LibraryKeyword
from robot.result import Keyword as KeywordResult
@@ -163,7 +163,7 @@ in the `__init__` method. Robot Framework 7.0 makes it possible to use string
that a listener can be specified as a class attribute and not only in `__init__`.
This is especially convenient when using the `@library` decorator:
-.. sourcecode:: python
+.. code:: python
from robot.api.deco import keyword, library
@@ -189,7 +189,7 @@ the syntax for creating variables in different scopes. Except for the mandatory
`VAR` marker, the syntax is also the same as when creating variables in the
Variables section. The syntax is best explained with examples:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -215,7 +215,7 @@ in the Variables section, it is possible to create also `@{list}` and `&{dict}`
variables. Unlike in the Variables section, variables can be created conditionally
using IF/ELSE structures:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Long value
@@ -259,7 +259,7 @@ accepts more arguments than there are embedded arguments, the remaining argument
can be passed in as normal arguments. This is illustrated by the following example
keyword:
-.. sourcecode:: python
+.. code:: python
@keyword('Number of ${animals} should be')
def example(animals, count):
@@ -267,7 +267,7 @@ keyword:
The above keyword could be used like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -291,7 +291,7 @@ In Python, the Literal__ type makes it possible to type arguments so that type
checkers accept only certain values. For example, a function like below
only accepts strings `x`, `y` and `z`.
-.. sourcecode:: python
+.. code:: python
def example(arg: Literal['x', 'y', 'z']):
...
@@ -330,7 +330,7 @@ Support for stringified generics and unions has now been added also to
Robot Framework's argument conversion (`#4711`_). For example,
the following typing now also works with Python 3.8:
-.. sourcecode:: python
+.. code:: python
def example(a: 'list[int]', b: 'int | float'):
...
@@ -349,7 +349,7 @@ section with `Test Tags` or `Keyword Tags` settings by using the `-tag` syntax
(`#4374`_). For example, tests `T1` and `T3` below are given tags `all` and
`most`, and test `T2` gets tags `all` and `one`:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Settings ***
Test Tags all most
@@ -466,7 +466,7 @@ are now stored to `start` and `elapsed` attributes and message times to `time`.
Examples:
-.. sourcecode:: xml
+.. code:: xml
Hello world!
@@ -498,7 +498,7 @@ the result model.
Examples:
-.. sourcecode:: xml
+.. code:: xml
...
diff --git a/doc/releasenotes/rf-7.0rc1.rst b/doc/releasenotes/rf-7.0rc1.rst
index 789f1183aa6..24870cbd161 100644
--- a/doc/releasenotes/rf-7.0rc1.rst
+++ b/doc/releasenotes/rf-7.0rc1.rst
@@ -96,7 +96,7 @@ version 3 has been extended to support also keywords and control structures (`#3
For example, a listener having the following methods prints information
about started keywords and ended WHILE loops:
-.. sourcecode:: python
+.. code:: python
from robot.running import Keyword as KeywordData, While as WhileData
from robot.result import Keyword as KeywordResult, While as WhileResult
@@ -115,7 +115,7 @@ With keywords it is possible to also get more information about the actually
executed keyword. For example, the following listener prints some information
about the executed keyword and the library it belongs to:
-.. sourcecode:: python
+.. code:: python
from robot.running import Keyword as KeywordData, LibraryKeyword
from robot.result import Keyword as KeywordResult
@@ -176,7 +176,7 @@ in the `__init__` method. Robot Framework 7.0 makes it possible to use string
that a listener can be specified as a class attribute and not only in `__init__`.
This is especially convenient when using the `@library` decorator:
-.. sourcecode:: python
+.. code:: python
from robot.api.deco import keyword, library
@@ -212,7 +212,7 @@ the syntax for creating variables in different scopes. Except for the mandatory
`VAR` marker, the syntax is also the same as when creating variables in the
Variables section. The syntax is best explained with examples:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -238,7 +238,7 @@ in the Variables section, it is possible to create also `@{list}` and `&{dict}`
variables. Unlike in the Variables section, variables can be created conditionally
using IF/ELSE structures:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Long value
@@ -282,7 +282,7 @@ accepts more arguments than there are embedded arguments, the remaining argument
can be passed in as normal arguments. This is illustrated by the following example
keyword:
-.. sourcecode:: python
+.. code:: python
@keyword('Number of ${animals} should be')
def example(animals, count):
@@ -290,7 +290,7 @@ keyword:
The above keyword could be used like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -349,7 +349,7 @@ In Python, the Literal__ type makes it possible to type arguments so that type
checkers accept only certain values. For example, this function only accepts
strings `x`, `y` and `z`:
-.. sourcecode:: python
+.. code:: python
def example(arg: Literal['x', 'y', 'z']):
...
@@ -388,7 +388,7 @@ Support for stringified generics and unions has now been added also to
Robot Framework's argument conversion (`#4711`_). For example,
the following typing now also works with Python 3.8:
-.. sourcecode:: python
+.. code:: python
def example(a: 'list[int]', b: 'int | float'):
...
@@ -408,7 +408,7 @@ the `-tag` syntax with their own `[Tags]` setting (`#4374`_). For example,
tests `T1` and `T3` below get tags `all` and `most`, and test `T2` gets
tags `all` and `one`:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Settings ***
Test Tags all most
@@ -532,7 +532,7 @@ are now stored to `start` and `elapsed` attributes and message times to `time`.
Examples:
-.. sourcecode:: xml
+.. code:: xml
Hello world!
@@ -564,7 +564,7 @@ the result model.
Examples:
-.. sourcecode:: xml
+.. code:: xml
...
diff --git a/doc/releasenotes/rf-7.0rc2.rst b/doc/releasenotes/rf-7.0rc2.rst
index e120e743fc3..b3f4c4b8d44 100644
--- a/doc/releasenotes/rf-7.0rc2.rst
+++ b/doc/releasenotes/rf-7.0rc2.rst
@@ -96,7 +96,7 @@ version 3 has been extended to support also keywords and control structures (`#3
For example, a listener having the following methods prints information
about started keywords and ended WHILE loops:
-.. sourcecode:: python
+.. code:: python
from robot.running import Keyword as KeywordData, While as WhileData
from robot.result import Keyword as KeywordResult, While as WhileResult
@@ -115,7 +115,7 @@ With keywords it is possible to also get more information about the actually
executed keyword. For example, the following listener prints some information
about the executed keyword and the library it belongs to:
-.. sourcecode:: python
+.. code:: python
from robot.running import Keyword as KeywordData, LibraryKeyword
from robot.result import Keyword as KeywordResult
@@ -176,7 +176,7 @@ in the `__init__` method. Robot Framework 7.0 makes it possible to use string
that a listener can be specified as a class attribute and not only in `__init__`.
This is especially convenient when using the `@library` decorator:
-.. sourcecode:: python
+.. code:: python
from robot.api.deco import keyword, library
@@ -219,7 +219,7 @@ the syntax for creating variables in different scopes. Except for the mandatory
`VAR` marker, the syntax is also the same as when creating variables in the
Variables section. The syntax is best explained with examples:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -245,7 +245,7 @@ in the Variables section, it is possible to create also `@{list}` and `&{dict}`
variables. Unlike in the Variables section, variables can be created conditionally
using IF/ELSE structures:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Long value
@@ -289,7 +289,7 @@ accepts more arguments than there are embedded arguments, the remaining argument
can be passed in as normal arguments. This is illustrated by the following example
keyword:
-.. sourcecode:: python
+.. code:: python
@keyword('Number of ${animals} should be')
def example(animals, count):
@@ -297,7 +297,7 @@ keyword:
The above keyword could be used like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -356,7 +356,7 @@ In Python, the Literal__ type makes it possible to type arguments so that type
checkers accept only certain values. For example, this function only accepts
strings `x`, `y` and `z`:
-.. sourcecode:: python
+.. code:: python
def example(arg: Literal['x', 'y', 'z']):
...
@@ -395,7 +395,7 @@ Support for stringified generics and unions has now been added also to
Robot Framework's argument conversion (`#4711`_). For example,
the following typing now also works with Python 3.8:
-.. sourcecode:: python
+.. code:: python
def example(a: 'list[int]', b: 'int | float'):
...
@@ -415,7 +415,7 @@ the `-tag` syntax with their own `[Tags]` setting (`#4374`_). For example,
tests `T1` and `T3` below get tags `all` and `most`, and test `T2` gets
tags `all` and `one`:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Settings ***
Test Tags all most
@@ -539,7 +539,7 @@ are now stored to `start` and `elapsed` attributes and message times to `time`.
Examples:
-.. sourcecode:: xml
+.. code:: xml
Hello world!
@@ -571,7 +571,7 @@ the result model.
Examples:
-.. sourcecode:: xml
+.. code:: xml
...
diff --git a/doc/releasenotes/rf-7.0rc3.rst b/doc/releasenotes/rf-7.0rc3.rst
index 30c077f1b8e..a98b2a2580d 100644
--- a/doc/releasenotes/rf-7.0rc3.rst
+++ b/doc/releasenotes/rf-7.0rc3.rst
@@ -96,7 +96,7 @@ version 3 has been extended to support also keywords and control structures (`#3
For example, a listener having the following methods prints information
about started keywords and ended WHILE loops:
-.. sourcecode:: python
+.. code:: python
from robot.running import Keyword as KeywordData, While as WhileData
from robot.result import Keyword as KeywordResult, While as WhileResult
@@ -115,7 +115,7 @@ With keywords it is possible to also get more information about the actually
executed keyword. For example, the following listener prints some information
about the executed keyword and the library it belongs to:
-.. sourcecode:: python
+.. code:: python
from robot.running import Keyword as KeywordData, LibraryKeyword
from robot.result import Keyword as KeywordResult
@@ -176,7 +176,7 @@ in the `__init__` method. Robot Framework 7.0 makes it possible to use string
that a listener can be specified as a class attribute and not only in `__init__`.
This is especially convenient when using the `@library` decorator:
-.. sourcecode:: python
+.. code:: python
from robot.api.deco import keyword, library
@@ -219,7 +219,7 @@ the syntax for creating variables in different scopes. Except for the mandatory
`VAR` marker, the syntax is also the same as when creating variables in the
Variables section. The syntax is best explained with examples:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -245,7 +245,7 @@ in the Variables section, it is possible to create also `@{list}` and `&{dict}`
variables. Unlike in the Variables section, variables can be created conditionally
using IF/ELSE structures:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Long value
@@ -289,7 +289,7 @@ accepts more arguments than there are embedded arguments, the remaining argument
can be passed in as normal arguments. This is illustrated by the following example
keyword:
-.. sourcecode:: python
+.. code:: python
@keyword('Number of ${animals} should be')
def example(animals, count):
@@ -297,7 +297,7 @@ keyword:
The above keyword could be used like this:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Test Cases ***
Example
@@ -356,7 +356,7 @@ In Python, the Literal__ type makes it possible to type arguments so that type
checkers accept only certain values. For example, this function only accepts
strings `x`, `y` and `z`:
-.. sourcecode:: python
+.. code:: python
def example(arg: Literal['x', 'y', 'z']):
...
@@ -395,7 +395,7 @@ Support for stringified generics and unions has now been added also to
Robot Framework's argument conversion (`#4711`_). For example,
the following typing now also works with Python 3.8:
-.. sourcecode:: python
+.. code:: python
def example(a: 'list[int]', b: 'int | float'):
...
@@ -415,7 +415,7 @@ the `-tag` syntax with their own `[Tags]` setting (`#4374`_). For example,
tests `T1` and `T3` below get tags `all` and `most`, and test `T2` gets
tags `all` and `one`:
-.. sourcecode:: robotframework
+.. code:: robotframework
*** Settings ***
Test Tags all most
@@ -539,7 +539,7 @@ are now stored to `start` and `elapsed` attributes and message times to `time`.
Examples:
-.. sourcecode:: xml
+.. code:: xml
Hello world!
@@ -571,7 +571,7 @@ the result model.
Examples:
-.. sourcecode:: xml
+.. code:: xml
...
diff --git a/doc/releasenotes/rf-7.1.1.rst b/doc/releasenotes/rf-7.1.1.rst
new file mode 100644
index 00000000000..dcf7f6c8c3c
--- /dev/null
+++ b/doc/releasenotes/rf-7.1.1.rst
@@ -0,0 +1,98 @@
+=====================
+Robot Framework 7.1.1
+=====================
+
+.. default-role:: code
+
+`Robot Framework`_ 7.1.1 is the first and also the only planned bug fix release
+in the Robot Framework 7.1.x series. It fixes all reported regressions in
+`Robot Framework 7.1 `_ as well as some issues affecting also
+earlier versions.
+
+Questions and comments related to the release can be sent to the `#devel`
+channel on `Robot Framework Slack`_ and possible bugs submitted to
+the `issue tracker`_.
+
+If you have pip_ installed, just run
+
+::
+
+ pip install --upgrade robotframework
+
+to install the latest available release or use
+
+::
+
+ pip install robotframework==7.1.1
+
+to install exactly this version. Alternatively, you can download the package
+from PyPI_ and install it manually. For more details and other installation
+approaches, see the `installation instructions`_.
+
+Robot Framework 7.1.1 was released on Saturday October 19, 2024.
+
+.. _Robot Framework: http://robotframework.org
+.. _Robot Framework Foundation: http://robotframework.org/foundation
+.. _pip: http://pip-installer.org
+.. _PyPI: https://pypi.python.org/pypi/robotframework
+.. _issue tracker: https://github.com/robotframework/robotframework/issues
+.. _Slack: http://slack.robotframework.org
+.. _Robot Framework Slack: Slack_
+.. _installation instructions: ../../INSTALL.rst
+
+.. contents::
+ :depth: 2
+ :local:
+
+Acknowledgements
+================
+
+Robot Framework development is sponsored by the `Robot Framework Foundation`_
+and its over 60 member organizations. If your organization is using Robot Framework
+and benefiting from it, consider joining the foundation to support its
+development as well.
+
+Big thanks to the Foundation and to everyone who has submitted bug reports, debugged
+problems, or otherwise helped with Robot Framework development.
+
+| `Pekka Klärck `_
+| Robot Framework lead developer
+
+Full list of fixes and enhancements
+===================================
+
+.. list-table::
+ :header-rows: 1
+
+ * - ID
+ - Type
+ - Priority
+ - Summary
+ * - `#5205`_
+ - bug
+ - high
+ - Execution fails at the end when using `--output NONE` and console hyperlinks are enabled
+ * - `#5224`_
+ - bug
+ - high
+ - Test is not failed if listener sets keyword status to fail and leaves message empty
+ * - `#5212`_
+ - bug
+ - medium
+ - Execution fails if standard streams are not available
+ * - `#5237`_
+ - bug
+ - medium
+ - Keyword timeout is not effective in teardown if keyword uses `Wait Until Keyword Succeeds`
+ * - `#5206`_
+ - enhancement
+ - medium
+ - Document that `output_file` listener method is called with `None` when using `--output NONE`
+
+Altogether 5 issues. View on the `issue tracker `__.
+
+.. _#5205: https://github.com/robotframework/robotframework/issues/5205
+.. _#5224: https://github.com/robotframework/robotframework/issues/5224
+.. _#5212: https://github.com/robotframework/robotframework/issues/5212
+.. _#5237: https://github.com/robotframework/robotframework/issues/5237
+.. _#5206: https://github.com/robotframework/robotframework/issues/5206
diff --git a/doc/releasenotes/rf-7.1.rst b/doc/releasenotes/rf-7.1.rst
new file mode 100644
index 00000000000..16433a97eaf
--- /dev/null
+++ b/doc/releasenotes/rf-7.1.rst
@@ -0,0 +1,336 @@
+===================
+Robot Framework 7.1
+===================
+
+.. default-role:: code
+
+`Robot Framework`_ 7.1 is a feature release with enhancements, for example,
+to listeners and to the `VAR` syntax that was introduced in `Robot Framework 7.0`_.
+
+Questions and comments related to the release can be sent to the `#devel`
+channel on `Robot Framework Slack`_ and possible bugs submitted to
+the `issue tracker`_.
+
+If you have pip_ installed, just run
+
+::
+
+ pip install --upgrade robotframework
+
+to install the latest available stable release or use
+
+::
+
+ pip install robotframework==7.1
+
+to install exactly this version. Alternatively you can download the package
+from PyPI_ and install it manually. For more details and other installation
+approaches, see the `installation instructions`_.
+
+Robot Framework 7.1 was released on Tuesday September 10, 2024.
+It has been superseded by `Robot Framework 7.1.1 `_
+
+.. _Robot Framework: http://robotframework.org
+.. _Robot Framework Foundation: http://robotframework.org/foundation
+.. _pip: http://pip-installer.org
+.. _PyPI: https://pypi.python.org/pypi/robotframework
+.. _issue tracker milestone: https://github.com/robotframework/robotframework/issues?q=milestone%3Av7.1
+.. _issue tracker: https://github.com/robotframework/robotframework/issues
+.. _robotframework-users: http://groups.google.com/group/robotframework-users
+.. _Slack: http://slack.robotframework.org
+.. _Robot Framework Slack: Slack_
+.. _installation instructions: ../../INSTALL.rst
+.. _Robot Framework 7.0: rf-7.0.rst
+
+.. contents::
+ :depth: 2
+ :local:
+
+Most important enhancements
+===========================
+
+Listener enhancements
+---------------------
+
+The listener interface was enhanced heavily in `Robot Framework 7.0`_
+and Robot Framework 7.1 contains some further improvements:
+
+- Listener calling order can be controlled with the `ROBOT_LISTENER_PRIORITY`
+ attribute (`#3473`_).
+
+- Listeners can change execution status (`#5090`_). For example, changing a keyword status
+ from PASS to FAIL used to affect only that keyword and execution continued normally, but
+ nowadays execution is stopped and the current test or task is failed.
+
+- Listener API version 3 got library, resource file and variable file import related
+ methods (`#5008`_). It now has all the same methods as listener API version 2.
+
+- Listeners can modify WHILE loop limits (`#5194`_).
+
+`VAR` enhancements
+------------------
+
+`Robot Framework 7.0`_ introduced the new `VAR` syntax for creating variables in different
+scopes using an uniform syntax. This syntax has been enhanced as follows:
+
+- The value of the created variable is logged similarly as when using `Set Test Variable`,
+ `Set Suite Variable` and other similar keywords (`#5077`_).
+
+- A new `SUITES` scope has been introduced to allow setting variables to the current
+ suite and to its child suites (`#5060`_). The existing `SUITE` scope only added
+ variables to the current suite, not to its children. This enhancement makes
+ the `VAR` syntax featurewise compatible with the `Set Suite Variable` keyword
+ that supports same functionality with slightly different syntax.
+
+Other enhancements
+------------------
+
+- Paths to log and report written to the console after execution are hyperlinks
+ making it easier to open them in a browser (`#5189`_). This requires the terminal
+ to support hyperlinks, but practically all Linux and OSX terminals support them
+ and although the classic `Windows Console`__ does not, the newer
+ `Windows Terminal`__ and most third-party terminals on Windows are compatible.
+
+- New API has been introduced for using named arguments programmatically (`#5143`_).
+ This API is targeted for pre-run modifiers and listeners that modify tests or tasks
+ before or during execution. There was an attempt to add such an API already in
+ Robot Framework 7.0 (`#5000`__), but the selected approach caused backwards
+ incompatibility problems and it was reverted in Robot Framework 7.0.1 (`#5031`__).
+ Hopefully this new API works better.
+
+- Korean translations have been added (`#5187`_) and Dutch translations have been
+ updated (`#5148`_).
+
+- If a keyword has a name like `${kind} example` and it is used like `Given good example`,
+ the variable `${kind}` will only contain value `good` when it used to contain `Given good`
+ (`#4577`_).
+
+- Robot Framework 7.1 is officially compatible with the forthcoming `Python 3.13`__
+ release (`#5091`_). No code changes were needed so also older Robot Framework
+ versions ought to work fine.
+
+__ https://en.wikipedia.org/wiki/Windows_Console
+__ https://en.wikipedia.org/wiki/Windows_Terminal
+__ https://github.com/robotframework/robotframework/issues/5000
+__ https://github.com/robotframework/robotframework/issues/5031
+__ https://docs.python.org/3.13/whatsnew/3.13.html
+
+Most important bug fixes
+------------------------
+
+- `List Should Contain Sub List` did not detect a failure if there was only one
+ different item and it was an empty string (`#5172`_).
+
+- Variables containing mutable values were resolved incorrectly in some cases
+ (`#5181`_). This had an effect that modifications to the created variable were
+ not seen properly.
+
+Backwards incompatible changes
+==============================
+
+- The `VAR` syntax nowadays logs the value of the created variable (`#5077`_) and this
+ can disclose confidential information. If this is a concern, it is possible to disable
+ logging all variable assignments with the `--max-assign-length` command line option.
+
+- Dutch translations have been updated (`#5148`_) and some of the old terms do not
+ work anymore. If this is a problem, users can create a custom language file that
+ contains the old variants. If there are wider problems, we can also look at changing
+ the localization system so that old terms would still work but cause a deprecation
+ warning.
+
+- As already mentioned above, the behavior when a keyword has a name like `${kind} example`
+ and it is used like `Given good example` has changed. Earlier the variable `${kind}`
+ contained `Given good`, but nowadays it contain only `good` (`#4577`_). This is a very
+ nice enhancement in general, but possible existing code handling the prefix that was
+ earlier included may need to be updated.
+
+Acknowledgements
+================
+
+Robot Framework development is sponsored by the `Robot Framework Foundation`_
+and its over 60 member organizations. If your organization is using Robot Framework
+and benefiting from it, consider joining the foundation to support its
+development as well.
+
+The community has also provided some great contributions:
+
+- `J. Foederer `__ enhanced the embedded argument
+ syntax so that possible BDD prefixes are not included if the keyword starts
+ with an embedded argument (`#4577`_) and also updated Dutch translations (`#5148`_).
+
+- `Hyeonho Kang `__ provided Korean translations (`#5187`_).
+
+- `Adrian Błasiak `_ made it possible to modify WHILE
+ loop limits by listeners (`#5194`_).
+
+- `@ChristopherJHart `__ added support for
+ `week` in time strings like `2 weeks 1 day` (`#5135`_).
+
+- `@wendi616 `__ enhanced the `Import Resource` keyword
+ so that it is executed in dry-run (`#3418`_).
+
+- `Peter `__ added default value support to the
+ `Get Selection From User` keyword (`#5038`_).
+
+- `Tatu Aalto `__ added generation time from output.xml
+ to the `Result` object (`#5087`_).
+
+- `@droeland `__ did the initial work to make
+ `Should Contain` work better with bytes (`#5054`_).
+
+Big thanks to Robot Framework Foundation, to community members listed above, and to
+everyone else who has tested preview releases, submitted bug reports, proposed
+enhancements, debugged problems, or otherwise helped with Robot Framework 7.1
+development.
+
+| `Pekka Klärck `_
+| Robot Framework lead developer
+
+Full list of fixes and enhancements
+===================================
+
+.. list-table::
+ :header-rows: 1
+
+ * - ID
+ - Type
+ - Priority
+ - Summary
+ * - `#3473`_
+ - enhancement
+ - critical
+ - Support controlling listener calling order with `ROBOT_LISTENER_PRIORITY` attribute
+ * - `#5090`_
+ - enhancement
+ - critical
+ - Allow listeners to change execution status
+ * - `#5091`_
+ - enhancement
+ - critical
+ - Python 3.13 compatibility
+ * - `#5094`_
+ - bug
+ - high
+ - Positional-only argument containing `=` is considered named argument if keyword accepts `**named`
+ * - `#5172`_
+ - bug
+ - high
+ - `List Should Contain Sub List` does not detect failure if only difference is empty string
+ * - `#5181`_
+ - bug
+ - high
+ - Variables containing mutable values are resolved incorrectly in some cases
+ * - `#4577`_
+ - enhancement
+ - high
+ - Strip prefix from argument value if BDD keyword starts with embedded argument
+ * - `#5008`_
+ - enhancement
+ - high
+ - Add library, resource file and variable file import related methods to listener version 3
+ * - `#5060`_
+ - enhancement
+ - high
+ - Support setting values for child suites with `VAR` syntax using `scope=SUITES`
+ * - `#5077`_
+ - enhancement
+ - high
+ - `VAR` syntax doesn't log the variable value like `Set * Variable` does
+ * - `#5143`_
+ - enhancement
+ - high
+ - New API for using named arguments programmatically
+ * - `#5187`_
+ - enhancement
+ - high
+ - Korean translation
+ * - `#5189`_
+ - enhancement
+ - high
+ - Make result file paths hyperlinks on terminal
+ * - `#5010`_
+ - bug
+ - medium
+ - Setting `PYTHONWARNDEFAULTENCODING` causes warnings
+ * - `#5151`_
+ - bug
+ - medium
+ - `Evaluate` keyword doesn't take attributes added into `builtins` module into account
+ * - `#5159`_
+ - bug
+ - medium
+ - Bad error message when using Rebot with a non-existing JSON output file
+ * - `#5177`_
+ - bug
+ - medium
+ - Rounding error leads to bad display of status color bar
+ * - `#3418`_
+ - enhancement
+ - medium
+ - `Import Resource` should be executed in dry-run
+ * - `#4821`_
+ - enhancement
+ - medium
+ - `Format String`: Allow using template strings containing `=` without escaping
+ * - `#5038`_
+ - enhancement
+ - medium
+ - Dialogs: Default option for `Get Selection From User`
+ * - `#5054`_
+ - enhancement
+ - medium
+ - Better support for bytes with `Should Contain`
+ * - `#5087`_
+ - enhancement
+ - medium
+ - Add generation time from output.xml to `Result` object
+ * - `#5135`_
+ - enhancement
+ - medium
+ - Add support for time strings containing `week` values
+ * - `#5148`_
+ - enhancement
+ - medium
+ - Updates to Dutch translations
+ * - `#5194`_
+ - enhancement
+ - medium
+ - Allow WHILE limit to be modified in listener V3
+ * - `#5169`_
+ - bug
+ - low
+ - Spaces are not normalized when matching keywords with embedded arguments
+ * - `#5200`_
+ - enhancement
+ - low
+ - Add `--console-width` value from CLI option to built-in variable `&{OPTIONS}`
+
+Altogether 27 issues. View on the `issue tracker `__.
+
+.. _#3473: https://github.com/robotframework/robotframework/issues/3473
+.. _#5090: https://github.com/robotframework/robotframework/issues/5090
+.. _#5091: https://github.com/robotframework/robotframework/issues/5091
+.. _#5094: https://github.com/robotframework/robotframework/issues/5094
+.. _#5172: https://github.com/robotframework/robotframework/issues/5172
+.. _#5181: https://github.com/robotframework/robotframework/issues/5181
+.. _#4577: https://github.com/robotframework/robotframework/issues/4577
+.. _#5008: https://github.com/robotframework/robotframework/issues/5008
+.. _#5060: https://github.com/robotframework/robotframework/issues/5060
+.. _#5077: https://github.com/robotframework/robotframework/issues/5077
+.. _#5143: https://github.com/robotframework/robotframework/issues/5143
+.. _#5187: https://github.com/robotframework/robotframework/issues/5187
+.. _#5189: https://github.com/robotframework/robotframework/issues/5189
+.. _#5010: https://github.com/robotframework/robotframework/issues/5010
+.. _#5151: https://github.com/robotframework/robotframework/issues/5151
+.. _#5159: https://github.com/robotframework/robotframework/issues/5159
+.. _#5177: https://github.com/robotframework/robotframework/issues/5177
+.. _#3418: https://github.com/robotframework/robotframework/issues/3418
+.. _#4821: https://github.com/robotframework/robotframework/issues/4821
+.. _#5038: https://github.com/robotframework/robotframework/issues/5038
+.. _#5054: https://github.com/robotframework/robotframework/issues/5054
+.. _#5087: https://github.com/robotframework/robotframework/issues/5087
+.. _#5135: https://github.com/robotframework/robotframework/issues/5135
+.. _#5148: https://github.com/robotframework/robotframework/issues/5148
+.. _#5194: https://github.com/robotframework/robotframework/issues/5194
+.. _#5169: https://github.com/robotframework/robotframework/issues/5169
+.. _#5200: https://github.com/robotframework/robotframework/issues/5200
diff --git a/doc/releasenotes/rf-7.1rc1.rst b/doc/releasenotes/rf-7.1rc1.rst
new file mode 100644
index 00000000000..cf6cd0ba906
--- /dev/null
+++ b/doc/releasenotes/rf-7.1rc1.rst
@@ -0,0 +1,330 @@
+=======================================
+Robot Framework 7.1 release candidate 1
+=======================================
+
+.. default-role:: code
+
+`Robot Framework`_ 7.1 is a feature release with enhancements, for example,
+to listeners and to the new `VAR` syntax. Robot Framework 7.1 release candidate 1
+contains all planned features and other changes.
+
+Questions and comments related to the release can be sent to the `#devel`
+channel on `Robot Framework Slack`_ and possible bugs submitted to
+the `issue tracker`_.
+
+If you have pip_ installed, just run
+
+::
+
+ pip install --pre --upgrade robotframework
+
+to install the latest available release or use
+
+::
+
+ pip install robotframework==7.1rc1
+
+to install exactly this version. Alternatively you can download the package
+from PyPI_ and install it manually. For more details and other installation
+approaches, see the `installation instructions`_.
+
+Robot Framework 7.1 rc 1 was released on Friday August 30, 2024. We hope that
+community members test it in their environments, so that we have a possibility
+to fix possible regressions still before the final release that is targeted
+for Monday September 9, 2024.
+
+.. _Robot Framework: http://robotframework.org
+.. _Robot Framework Foundation: http://robotframework.org/foundation
+.. _pip: http://pip-installer.org
+.. _PyPI: https://pypi.python.org/pypi/robotframework
+.. _issue tracker milestone: https://github.com/robotframework/robotframework/issues?q=milestone%3Av7.1
+.. _issue tracker: https://github.com/robotframework/robotframework/issues
+.. _robotframework-users: http://groups.google.com/group/robotframework-users
+.. _Slack: http://slack.robotframework.org
+.. _Robot Framework Slack: Slack_
+.. _installation instructions: ../../INSTALL.rst
+.. _Robot Framework 7.0: rf-7.0.rst
+
+
+.. contents::
+ :depth: 2
+ :local:
+
+Most important enhancements
+===========================
+
+Listener enhancements
+---------------------
+
+The listener interface was enhanced heavily in `Robot Framework 7.0`_
+and Robot Framework 7.1 contains some further improvements:
+
+- Listener calling order can be controlled with the `ROBOT_LISTENER_PRIORITY`
+ attribute (`#3473`_).
+
+- Listeners can change execution status (`#5090`_). For example, changing a keyword status
+ from PASS to FAIL used to affect only that keyword and execution continued normally, but
+ nowadays execution is stopped and the current test or task is failed.
+
+- Listener API version 3 got library, resource file and variable file import related
+ methods (`#5008`_) and now has all the same methods as listener API version 2.
+
+`VAR` enhancements
+------------------
+
+`Robot Framework 7.0`_ introduced the new `VAR` syntax for creating variables in different
+scopes using an uniform syntax. That syntax has now been enhanced:
+
+- The value of the created variable is logged similarly as when using `Set Test Variable`,
+ `Set Suite Variable` and other similar keywords (`#5077`_).
+
+- A new `SUITES` scope has been introduced to allow setting variables to the current
+ suite and to its child suites (`#5060`_). The existing `SUITE` scope only adds
+ variables to the current suite, not to its children. This enhancement makes
+ the `VAR` syntax featurewise compatible with the `Set Suite Variable` keyword
+ that supports same functionality with slightly different syntax.
+
+Other enhancements
+------------------
+
+- Paths to log and report written to the console after execution are hyperlinks
+ making it easier to open them in a browser (`#5189`_). This requires the terminal
+ to support hyperlinks, but practically all Linux and OSX terminals support them
+ and although the classic `Windows Console`__ does not, the newer
+ `Windows Terminal`__ and most third-party terminals on Windows are compatible.
+
+- New API has been introduced for using named arguments programmatically (`#5143`_).
+ This API is targeted for pre-run modifiers and listeners that modify tests or tasks
+ before or during execution. There was an attempt to add such an API already in
+ Robot Framework 7.0 (`#5000`__), but the selected approach caused backwards
+ incompatibility problems and it was reverted in Robot Framework 7.1 (`#5031`__).
+ Hopefully this new API works better.
+
+- Korean translation has been added (`#5187`_) and Dutch translation has been
+ updated (`#5148`_).
+
+- Robot Framework 7.1 is officially compatible with the forthcoming `Python 3.13`__
+ release (`#5091`_). No code changes were needed so also older Robot Framework
+ versions ought to work fine.
+
+__ https://en.wikipedia.org/wiki/Windows_Console
+__ https://en.wikipedia.org/wiki/Windows_Terminal
+__ https://github.com/robotframework/robotframework/issues/5000
+__ https://github.com/robotframework/robotframework/issues/5031
+__ https://docs.python.org/3.13/whatsnew/3.13.html
+
+Backwards incompatible changes
+==============================
+
+- The `VAR` syntax nowadays logs the value of the created variable (`#5077`_) and this
+ can disclose confidential information. If this is a concern, it is possible to disable
+ logging all variable assignments with the `--max-assign-length` command line option.
+
+- Dutch translation has been updated (`#5148`_) and some of the old terms do not
+ work anymore. If this is a problem, users can create a custom language file that
+ contains the old variants. If there are wider problems, we can also look at changing
+ the localization system so that old terms would still work but cause a deprecation
+ warning.
+
+- If a keyword has a name like `${kind} example` and it is used like `Given good example`,
+ the variable `${kind}` will only contain value `good` when it used to contain `Given good`
+ (`#4577`_). This is obviously a nice enhancement in general, but possible existing code
+ handling the prefix that was earlier included may need to be updated.
+
+Acknowledgements
+================
+
+Robot Framework development is sponsored by the `Robot Framework Foundation`_
+and its over 60 member organizations. If your organization is using Robot Framework
+and benefiting from it, consider joining the foundation to support its
+development as well.
+
+The community has also provided some great contributions:
+
+- `J. Foederer `__ enhanced embedded argument
+ syntax so that possible BDD prefixes are not included if the keyword starts
+ with an embedded argument (`#4577`_) and updated Dutch translations (`#5148`_).
+
+- `Hyeonho Kang `__ provided Korean translation (`#5187`_).
+
+- `@ChristopherJHart `__ added support for
+ `week` in time strings like `2 weeks 1 day` (`#5135`_).
+
+- `@wendi616 `__ enhanced the `Import Resource` keyword
+ so that it is executed in dry-run (`#3418`_).
+
+- `Peter `__ added default value support to the
+ `Get Selection From User` keyword (`#5038`_).
+
+- `Tatu Aalto `__ added generation time from output.xml
+ to the `Result` object (`#5087`_).
+
+- `@droeland `__ did the initial work to make
+ `Should Contain` work better with bytes (`#5054`_).
+
+Big thanks to Robot Framework Foundation, to community members listed above, and to
+everyone else who has tested preview releases, submitted bug reports, proposed
+enhancements, debugged problems, or otherwise helped with Robot Framework 7.1
+development.
+
+| `Pekka Klärck `_
+| Robot Framework lead developer
+
+Full list of fixes and enhancements
+===================================
+
+.. list-table::
+ :header-rows: 1
+
+ * - ID
+ - Type
+ - Priority
+ - Summary
+ - Added
+ * - `#3473`_
+ - enhancement
+ - critical
+ - Support controlling listener calling order with `ROBOT_LISTENER_PRIORITY` attribute
+ - rc 1
+ * - `#5090`_
+ - enhancement
+ - critical
+ - Allow listeners to change execution status
+ - rc 1
+ * - `#5091`_
+ - enhancement
+ - critical
+ - Python 3.13 compatibility
+ - rc 1
+ * - `#5094`_
+ - bug
+ - high
+ - Positional-only argument containing `=` is considered named argument if keyword accepts `**named`
+ - rc 1
+ * - `#5181`_
+ - bug
+ - high
+ - Variables containing mutable values are resolved incorrectly in some cases
+ - rc 1
+ * - `#5008`_
+ - enhancement
+ - high
+ - Add library, resource file and variable file import related methods to listener version 3
+ - rc 1
+ * - `#5060`_
+ - enhancement
+ - high
+ - Support setting values for child suites with `VAR` syntax using `scope=SUITES`
+ - rc 1
+ * - `#5077`_
+ - enhancement
+ - high
+ - `VAR` syntax doesn't log the variable value like `Set * Variable` does
+ - rc 1
+ * - `#5143`_
+ - enhancement
+ - high
+ - New API for using named arguments programmatically
+ - rc 1
+ * - `#5187`_
+ - enhancement
+ - high
+ - Korean translation
+ - rc 1
+ * - `#5189`_
+ - enhancement
+ - high
+ - Make result file paths hyperlinks on terminal
+ - rc 1
+ * - `#5010`_
+ - bug
+ - medium
+ - Setting `PYTHONWARNDEFAULTENCODING` causes warnings
+ - rc 1
+ * - `#5151`_
+ - bug
+ - medium
+ - `Evaluate` keyword doesn't take attributes added into `builtins` module into account
+ - rc 1
+ * - `#5159`_
+ - bug
+ - medium
+ - Bad error message when using Rebot with a non-existing JSON output file
+ - rc 1
+ * - `#5177`_
+ - bug
+ - medium
+ - Rounding error leads to bad display of status color bar
+ - rc 1
+ * - `#3418`_
+ - enhancement
+ - medium
+ - `Import Resource` should be executed in dry-run
+ - rc 1
+ * - `#4577`_
+ - enhancement
+ - medium
+ - Strip prefix from argument value if BDD keyword starts with embedded argument
+ - rc 1
+ * - `#4821`_
+ - enhancement
+ - medium
+ - `Format String`: Allow using template strings containing `=` without escaping
+ - rc 1
+ * - `#5038`_
+ - enhancement
+ - medium
+ - Dialogs: Default option for `Get Selection From User`
+ - rc 1
+ * - `#5054`_
+ - enhancement
+ - medium
+ - Better support for bytes with `Should Contain`
+ - rc 1
+ * - `#5087`_
+ - enhancement
+ - medium
+ - Add generation time from output.xml to `Result` object
+ - rc 1
+ * - `#5135`_
+ - enhancement
+ - medium
+ - Add support for time strings containing `week` values
+ - rc 1
+ * - `#5148`_
+ - enhancement
+ - medium
+ - Updates to Dutch translations
+ - rc 1
+ * - `#5169`_
+ - bug
+ - low
+ - Spaces are not normalized when matching keywords with embedded arguments
+ - rc 1
+
+Altogether 24 issues. View on the `issue tracker `__.
+
+.. _#3473: https://github.com/robotframework/robotframework/issues/3473
+.. _#5090: https://github.com/robotframework/robotframework/issues/5090
+.. _#5091: https://github.com/robotframework/robotframework/issues/5091
+.. _#5094: https://github.com/robotframework/robotframework/issues/5094
+.. _#5181: https://github.com/robotframework/robotframework/issues/5181
+.. _#5008: https://github.com/robotframework/robotframework/issues/5008
+.. _#5060: https://github.com/robotframework/robotframework/issues/5060
+.. _#5077: https://github.com/robotframework/robotframework/issues/5077
+.. _#5143: https://github.com/robotframework/robotframework/issues/5143
+.. _#5187: https://github.com/robotframework/robotframework/issues/5187
+.. _#5189: https://github.com/robotframework/robotframework/issues/5189
+.. _#5010: https://github.com/robotframework/robotframework/issues/5010
+.. _#5151: https://github.com/robotframework/robotframework/issues/5151
+.. _#5159: https://github.com/robotframework/robotframework/issues/5159
+.. _#5177: https://github.com/robotframework/robotframework/issues/5177
+.. _#3418: https://github.com/robotframework/robotframework/issues/3418
+.. _#4577: https://github.com/robotframework/robotframework/issues/4577
+.. _#4821: https://github.com/robotframework/robotframework/issues/4821
+.. _#5038: https://github.com/robotframework/robotframework/issues/5038
+.. _#5054: https://github.com/robotframework/robotframework/issues/5054
+.. _#5087: https://github.com/robotframework/robotframework/issues/5087
+.. _#5135: https://github.com/robotframework/robotframework/issues/5135
+.. _#5148: https://github.com/robotframework/robotframework/issues/5148
+.. _#5169: https://github.com/robotframework/robotframework/issues/5169
diff --git a/doc/releasenotes/rf-7.1rc2.rst b/doc/releasenotes/rf-7.1rc2.rst
new file mode 100644
index 00000000000..48a425edf22
--- /dev/null
+++ b/doc/releasenotes/rf-7.1rc2.rst
@@ -0,0 +1,342 @@
+=======================================
+Robot Framework 7.1 release candidate 2
+=======================================
+
+.. default-role:: code
+
+`Robot Framework`_ 7.1 is a feature release with enhancements, for example,
+to listeners and to the `VAR` syntax that was introduced in Robot Framework 7.0.
+This second release candidate contains all planned features and other changes.
+Changes after the `first release candidate `_ are related to
+console hyper links (`#5189`_) and modifying WHILE loop limits by listeners (`#5194`_).
+
+Questions and comments related to the release can be sent to the `#devel`
+channel on `Robot Framework Slack`_ and possible bugs submitted to
+the `issue tracker`_.
+
+If you have pip_ installed, just run
+
+::
+
+ pip install --pre --upgrade robotframework
+
+to install the latest available release or use
+
+::
+
+ pip install robotframework==7.1rc2
+
+to install exactly this version. Alternatively you can download the package
+from PyPI_ and install it manually. For more details and other installation
+approaches, see the `installation instructions`_.
+
+Robot Framework 7.1 rc 2 was released on Tuesday September 3, 2024. We hope that
+community members can test it in their environments, so that we have a possibility
+to fix possible regressions still before the final release that is targeted
+for Monday September 9, 2024.
+
+.. _Robot Framework: http://robotframework.org
+.. _Robot Framework Foundation: http://robotframework.org/foundation
+.. _pip: http://pip-installer.org
+.. _PyPI: https://pypi.python.org/pypi/robotframework
+.. _issue tracker milestone: https://github.com/robotframework/robotframework/issues?q=milestone%3Av7.1
+.. _issue tracker: https://github.com/robotframework/robotframework/issues
+.. _robotframework-users: http://groups.google.com/group/robotframework-users
+.. _Slack: http://slack.robotframework.org
+.. _Robot Framework Slack: Slack_
+.. _installation instructions: ../../INSTALL.rst
+.. _Robot Framework 7.0: rf-7.0.rst
+
+.. contents::
+ :depth: 2
+ :local:
+
+Most important enhancements
+===========================
+
+Listener enhancements
+---------------------
+
+The listener interface was enhanced heavily in `Robot Framework 7.0`_
+and Robot Framework 7.1 contains some further improvements:
+
+- Listener calling order can be controlled with the `ROBOT_LISTENER_PRIORITY`
+ attribute (`#3473`_).
+
+- Listeners can change execution status (`#5090`_). For example, changing a keyword status
+ from PASS to FAIL used to affect only that keyword and execution continued normally, but
+ nowadays execution is stopped and the current test or task is failed.
+
+- Listener API version 3 got library, resource file and variable file import related
+ methods (`#5008`_). It now has all the same methods as listener API version 2.
+
+- Listeners can nowadays modify WHILE loop limits (`#5194`_).
+
+`VAR` enhancements
+------------------
+
+`Robot Framework 7.0`_ introduced the new `VAR` syntax for creating variables in different
+scopes using an uniform syntax. This syntax has been enhanced as follows:
+
+- The value of the created variable is logged similarly as when using `Set Test Variable`,
+ `Set Suite Variable` and other similar keywords (`#5077`_).
+
+- A new `SUITES` scope has been introduced to allow setting variables to the current
+ suite and to its child suites (`#5060`_). The existing `SUITE` scope only added
+ variables to the current suite, not to its children. This enhancement makes
+ the `VAR` syntax featurewise compatible with the `Set Suite Variable` keyword
+ that supports same functionality with slightly different syntax.
+
+Other enhancements
+------------------
+
+- Paths to log and report written to the console after execution are hyperlinks
+ making it easier to open them in a browser (`#5189`_). This requires the terminal
+ to support hyperlinks, but practically all Linux and OSX terminals support them
+ and although the classic `Windows Console`__ does not, the newer
+ `Windows Terminal`__ and most third-party terminals on Windows are compatible.
+
+- New API has been introduced for using named arguments programmatically (`#5143`_).
+ This API is targeted for pre-run modifiers and listeners that modify tests or tasks
+ before or during execution. There was an attempt to add such an API already in
+ Robot Framework 7.0 (`#5000`__), but the selected approach caused backwards
+ incompatibility problems and it was reverted in Robot Framework 7.1 (`#5031`__).
+ Hopefully this new API works better.
+
+- Korean translations have been added (`#5187`_) and Dutch translations have been
+ updated (`#5148`_).
+
+- Robot Framework 7.1 is officially compatible with the forthcoming `Python 3.13`__
+ release (`#5091`_). No code changes were needed so also older Robot Framework
+ versions ought to work fine.
+
+__ https://en.wikipedia.org/wiki/Windows_Console
+__ https://en.wikipedia.org/wiki/Windows_Terminal
+__ https://github.com/robotframework/robotframework/issues/5000
+__ https://github.com/robotframework/robotframework/issues/5031
+__ https://docs.python.org/3.13/whatsnew/3.13.html
+
+Backwards incompatible changes
+==============================
+
+- The `VAR` syntax nowadays logs the value of the created variable (`#5077`_) and this
+ can disclose confidential information. If this is a concern, it is possible to disable
+ logging all variable assignments with the `--max-assign-length` command line option.
+
+- Dutch translations have been updated (`#5148`_) and some of the old terms do not
+ work anymore. If this is a problem, users can create a custom language file that
+ contains the old variants. If there are wider problems, we can also look at changing
+ the localization system so that old terms would still work but cause a deprecation
+ warning.
+
+- If a keyword has a name like `${kind} example` and it is used like `Given good example`,
+ the variable `${kind}` will only contain value `good` when it used to contain `Given good`
+ (`#4577`_). This is obviously a nice enhancement in general, but possible existing code
+ handling the prefix that was earlier included may need to be updated.
+
+Acknowledgements
+================
+
+Robot Framework development is sponsored by the `Robot Framework Foundation`_
+and its over 60 member organizations. If your organization is using Robot Framework
+and benefiting from it, consider joining the foundation to support its
+development as well.
+
+The community has also provided some great contributions:
+
+- `J. Foederer `__ enhanced the embedded argument
+ syntax so that possible BDD prefixes are not included if the keyword starts
+ with an embedded argument (`#4577`_) and also updated Dutch translations (`#5148`_).
+
+- `Hyeonho Kang `__ provided Korean translations (`#5187`_).
+
+- `Adrian Błasiak `_ made it possible to modify WHILE
+ loop limits by listeners (`#5194`_).
+
+- `@ChristopherJHart `__ added support for
+ `week` in time strings like `2 weeks 1 day` (`#5135`_).
+
+- `@wendi616 `__ enhanced the `Import Resource` keyword
+ so that it is executed in dry-run (`#3418`_).
+
+- `Peter `__ added default value support to the
+ `Get Selection From User` keyword (`#5038`_).
+
+- `Tatu Aalto `__ added generation time from output.xml
+ to the `Result` object (`#5087`_).
+
+- `@droeland `__ did the initial work to make
+ `Should Contain` work better with bytes (`#5054`_).
+
+Big thanks to Robot Framework Foundation, to community members listed above, and to
+everyone else who has tested preview releases, submitted bug reports, proposed
+enhancements, debugged problems, or otherwise helped with Robot Framework 7.1
+development.
+
+| `Pekka Klärck `_
+| Robot Framework lead developer
+
+Full list of fixes and enhancements
+===================================
+
+.. list-table::
+ :header-rows: 1
+
+ * - ID
+ - Type
+ - Priority
+ - Summary
+ - Added
+ * - `#3473`_
+ - enhancement
+ - critical
+ - Support controlling listener calling order with `ROBOT_LISTENER_PRIORITY` attribute
+ - rc 1
+ * - `#5090`_
+ - enhancement
+ - critical
+ - Allow listeners to change execution status
+ - rc 1
+ * - `#5091`_
+ - enhancement
+ - critical
+ - Python 3.13 compatibility
+ - rc 1
+ * - `#5094`_
+ - bug
+ - high
+ - Positional-only argument containing `=` is considered named argument if keyword accepts `**named`
+ - rc 1
+ * - `#5181`_
+ - bug
+ - high
+ - Variables containing mutable values are resolved incorrectly in some cases
+ - rc 1
+ * - `#5008`_
+ - enhancement
+ - high
+ - Add library, resource file and variable file import related methods to listener version 3
+ - rc 1
+ * - `#5060`_
+ - enhancement
+ - high
+ - Support setting values for child suites with `VAR` syntax using `scope=SUITES`
+ - rc 1
+ * - `#5077`_
+ - enhancement
+ - high
+ - `VAR` syntax doesn't log the variable value like `Set * Variable` does
+ - rc 1
+ * - `#5143`_
+ - enhancement
+ - high
+ - New API for using named arguments programmatically
+ - rc 1
+ * - `#5187`_
+ - enhancement
+ - high
+ - Korean translation
+ - rc 1
+ * - `#5189`_
+ - enhancement
+ - high
+ - Make result file paths hyperlinks on terminal
+ - rc 1
+ * - `#5010`_
+ - bug
+ - medium
+ - Setting `PYTHONWARNDEFAULTENCODING` causes warnings
+ - rc 1
+ * - `#5151`_
+ - bug
+ - medium
+ - `Evaluate` keyword doesn't take attributes added into `builtins` module into account
+ - rc 1
+ * - `#5159`_
+ - bug
+ - medium
+ - Bad error message when using Rebot with a non-existing JSON output file
+ - rc 1
+ * - `#5177`_
+ - bug
+ - medium
+ - Rounding error leads to bad display of status color bar
+ - rc 1
+ * - `#3418`_
+ - enhancement
+ - medium
+ - `Import Resource` should be executed in dry-run
+ - rc 1
+ * - `#4577`_
+ - enhancement
+ - medium
+ - Strip prefix from argument value if BDD keyword starts with embedded argument
+ - rc 1
+ * - `#4821`_
+ - enhancement
+ - medium
+ - `Format String`: Allow using template strings containing `=` without escaping
+ - rc 1
+ * - `#5038`_
+ - enhancement
+ - medium
+ - Dialogs: Default option for `Get Selection From User`
+ - rc 1
+ * - `#5054`_
+ - enhancement
+ - medium
+ - Better support for bytes with `Should Contain`
+ - rc 1
+ * - `#5087`_
+ - enhancement
+ - medium
+ - Add generation time from output.xml to `Result` object
+ - rc 1
+ * - `#5135`_
+ - enhancement
+ - medium
+ - Add support for time strings containing `week` values
+ - rc 1
+ * - `#5148`_
+ - enhancement
+ - medium
+ - Updates to Dutch translations
+ - rc 1
+ * - `#5194`_
+ - enhancement
+ - medium
+ - Allow WHILE limit to be modified in listener V3
+ - rc 2
+ * - `#5169`_
+ - bug
+ - low
+ - Spaces are not normalized when matching keywords with embedded arguments
+ - rc 1
+
+Altogether 25 issues. View on the `issue tracker `__.
+
+.. _#3473: https://github.com/robotframework/robotframework/issues/3473
+.. _#5090: https://github.com/robotframework/robotframework/issues/5090
+.. _#5091: https://github.com/robotframework/robotframework/issues/5091
+.. _#5094: https://github.com/robotframework/robotframework/issues/5094
+.. _#5181: https://github.com/robotframework/robotframework/issues/5181
+.. _#5008: https://github.com/robotframework/robotframework/issues/5008
+.. _#5060: https://github.com/robotframework/robotframework/issues/5060
+.. _#5077: https://github.com/robotframework/robotframework/issues/5077
+.. _#5143: https://github.com/robotframework/robotframework/issues/5143
+.. _#5187: https://github.com/robotframework/robotframework/issues/5187
+.. _#5189: https://github.com/robotframework/robotframework/issues/5189
+.. _#5010: https://github.com/robotframework/robotframework/issues/5010
+.. _#5151: https://github.com/robotframework/robotframework/issues/5151
+.. _#5159: https://github.com/robotframework/robotframework/issues/5159
+.. _#5177: https://github.com/robotframework/robotframework/issues/5177
+.. _#3418: https://github.com/robotframework/robotframework/issues/3418
+.. _#4577: https://github.com/robotframework/robotframework/issues/4577
+.. _#4821: https://github.com/robotframework/robotframework/issues/4821
+.. _#5038: https://github.com/robotframework/robotframework/issues/5038
+.. _#5054: https://github.com/robotframework/robotframework/issues/5054
+.. _#5087: https://github.com/robotframework/robotframework/issues/5087
+.. _#5135: https://github.com/robotframework/robotframework/issues/5135
+.. _#5148: https://github.com/robotframework/robotframework/issues/5148
+.. _#5194: https://github.com/robotframework/robotframework/issues/5194
+.. _#5169: https://github.com/robotframework/robotframework/issues/5169
diff --git a/doc/releasenotes/rf-7.2.1.rst b/doc/releasenotes/rf-7.2.1.rst
new file mode 100644
index 00000000000..6a9f6bd813f
--- /dev/null
+++ b/doc/releasenotes/rf-7.2.1.rst
@@ -0,0 +1,116 @@
+=====================
+Robot Framework 7.2.1
+=====================
+
+.. default-role:: code
+
+`Robot Framework`_ 7.2.1 is the first bug fix release in the Robot Framework 7.2.x
+series. It fixes all reported regressions in `Robot Framework 7.2 `_
+as well as some issues affecting also earlier versions. Unfortunately the
+there was a mistake in the build process that required creating an immediate
+`Robot Framework 7.2.2 `_ release.
+
+Questions and comments related to the release can be sent to the `#devel`
+channel on `Robot Framework Slack`_ and possible bugs submitted to
+the `issue tracker`_.
+
+If you have pip_ installed, just run
+
+::
+
+ pip install --pre --upgrade robotframework
+
+to install the latest available release or use
+
+::
+
+ pip install robotframework==7.2.1
+
+to install exactly this version. Alternatively you can download the package
+from PyPI_ and install it manually. For more details and other installation
+approaches, see the `installation instructions`_.
+
+Robot Framework 7.2.1 was released on Friday February 7, 2025.
+It has been superseded by `Robot Framework 7.2.2 `_.
+
+.. _Robot Framework: http://robotframework.org
+.. _Robot Framework Foundation: http://robotframework.org/foundation
+.. _pip: http://pip-installer.org
+.. _PyPI: https://pypi.python.org/pypi/robotframework
+.. _issue tracker milestone: https://github.com/robotframework/robotframework/issues?q=milestone%3Av7.2.1
+.. _issue tracker: https://github.com/robotframework/robotframework/issues
+.. _robotframework-users: http://groups.google.com/group/robotframework-users
+.. _Slack: http://slack.robotframework.org
+.. _Robot Framework Slack: Slack_
+.. _installation instructions: ../../INSTALL.rst
+
+.. contents::
+ :depth: 2
+ :local:
+
+Acknowledgements
+================
+
+Robot Framework development is sponsored by the `Robot Framework Foundation`_
+and its over 70 member organizations. If your organization is using Robot Framework
+and benefiting from it, consider joining the foundation to support its development
+as well.
+
+In addition to the work sponsored by the foundation, this release got a contribution
+from `Mohd Maaz Usmani `_ who fixed `Lists Should Be Equal`
+when used with `ignore_case` and `ignore_order` arguments (`#5321`_).
+
+Big thanks to the Foundation and to everyone who has submitted bug reports, debugged
+problems, or otherwise helped with Robot Framework development.
+
+| `Pekka Klärck `_
+| Robot Framework lead developer
+
+Full list of fixes and enhancements
+===================================
+
+.. list-table::
+ :header-rows: 1
+
+ * - ID
+ - Type
+ - Priority
+ - Summary
+ * - `#5326`_
+ - bug
+ - critical
+ - Messages in test body cause crash when using templates and some iterations are skipped
+ * - `#5317`_
+ - bug
+ - high
+ - Libdoc's default language selection does not support all available languages
+ * - `#5318`_
+ - bug
+ - high
+ - Log and report generation crashes if `--removekeywords` is used with `PASSED` or `ALL` and test body contains messages
+ * - `#5058`_
+ - bug
+ - medium
+ - Elapsed time is not updated when merging results
+ * - `#5321`_
+ - bug
+ - medium
+ - `Lists Should Be Equal` does not work as expected with `ignore_case` and `ignore_order` arguments
+ * - `#5331`_
+ - bug
+ - medium
+ - `BuiltIn.set_global/suite/test/local_variable` should not log if used by listener and no keyword is started
+ * - `#5325`_
+ - bug
+ - low
+ - Elapsed time is ignored when parsing output.xml if start time is not set
+
+Altogether 7 issues. View on the `issue tracker `__.
+
+.. _#5326: https://github.com/robotframework/robotframework/issues/5326
+.. _#5317: https://github.com/robotframework/robotframework/issues/5317
+.. _#5318: https://github.com/robotframework/robotframework/issues/5318
+.. _#5058: https://github.com/robotframework/robotframework/issues/5058
+.. _#5321: https://github.com/robotframework/robotframework/issues/5321
+.. _#5331: https://github.com/robotframework/robotframework/issues/5331
+.. _#5325: https://github.com/robotframework/robotframework/issues/5325
diff --git a/doc/releasenotes/rf-7.2.2.rst b/doc/releasenotes/rf-7.2.2.rst
new file mode 100644
index 00000000000..464ccd3450d
--- /dev/null
+++ b/doc/releasenotes/rf-7.2.2.rst
@@ -0,0 +1,79 @@
+=====================
+Robot Framework 7.2.2
+=====================
+
+.. default-role:: code
+
+`Robot Framework`_ 7.2.2 is the second and the last planned bug fix release
+in the Robot Framework 7.2.x series. It fixes a mistake made when releasing
+`Robot Framework 7.2.1 `_.
+
+Questions and comments related to the release can be sent to the `#devel`
+channel on `Robot Framework Slack`_ and possible bugs submitted to
+the `issue tracker`_.
+
+If you have pip_ installed, just run
+
+::
+
+ pip install --pre --upgrade robotframework
+
+to install the latest available release or use
+
+::
+
+ pip install robotframework==7.2.2
+
+to install exactly this version. Alternatively you can download the package
+from PyPI_ and install it manually. For more details and other installation
+approaches, see the `installation instructions`_.
+
+Robot Framework 7.2.2 was released on Friday February 7, 2025.
+
+.. _Robot Framework: http://robotframework.org
+.. _Robot Framework Foundation: http://robotframework.org/foundation
+.. _pip: http://pip-installer.org
+.. _PyPI: https://pypi.python.org/pypi/robotframework
+.. _issue tracker milestone: https://github.com/robotframework/robotframework/issues?q=milestone%3Av7.2.2
+.. _issue tracker: https://github.com/robotframework/robotframework/issues
+.. _robotframework-users: http://groups.google.com/group/robotframework-users
+.. _Slack: http://slack.robotframework.org
+.. _Robot Framework Slack: Slack_
+.. _installation instructions: ../../INSTALL.rst
+
+.. contents::
+ :depth: 2
+ :local:
+
+Acknowledgements
+================
+
+Robot Framework development is sponsored by the `Robot Framework Foundation`_
+and its over 70 member organizations. If your organization is using Robot Framework
+and benefiting from it, consider joining the foundation to support its development
+as well.
+
+Big thanks to the Foundation and to everyone who has submitted bug reports, debugged
+problems, or otherwise helped with Robot Framework development.
+
+| `Pekka Klärck `_
+| Robot Framework lead developer
+
+Full list of fixes and enhancements
+===================================
+
+.. list-table::
+ :header-rows: 1
+
+ * - ID
+ - Type
+ - Priority
+ - Summary
+ * - `#5329`_
+ - bug
+ - medium
+ - New Libdoc language selection button does not work well on mobile
+
+Altogether 1 issue. View on the `issue tracker `__.
+
+.. _#5329: https://github.com/robotframework/robotframework/issues/5329
diff --git a/doc/releasenotes/rf-7.2.rst b/doc/releasenotes/rf-7.2.rst
new file mode 100644
index 00000000000..27acb2610b1
--- /dev/null
+++ b/doc/releasenotes/rf-7.2.rst
@@ -0,0 +1,645 @@
+===================
+Robot Framework 7.2
+===================
+
+.. default-role:: code
+
+`Robot Framework`_ 7.2 is a feature release with JSON output support (`#3423`_),
+`GROUP` syntax for grouping keywords and control structures (`#5257`_), new
+Libdoc technology (`#4304`_) including translations (`#3676`_), and various
+other features.
+
+Questions and comments related to the release can be sent to the `#devel`
+channel on `Robot Framework Slack`_ and possible bugs submitted to
+the `issue tracker`_.
+
+If you have pip_ installed, just run
+
+::
+
+ pip install --upgrade robotframework
+
+to install the latest available release or use
+
+::
+
+ pip install robotframework==7.2
+
+to install exactly this version. Alternatively you can download the package
+from PyPI_ and install it manually. For more details and other installation
+approaches, see the `installation instructions`_.
+
+Robot Framework 7.2 was released on Tuesday January 14, 2025.
+It has been superseded by `Robot Framework 7.2.1 `_ and
+`Robot Framework 7.2.2 `_.
+
+.. _Robot Framework: http://robotframework.org
+.. _Robot Framework Foundation: http://robotframework.org/foundation
+.. _pip: http://pip-installer.org
+.. _PyPI: https://pypi.python.org/pypi/robotframework
+.. _issue tracker milestone: https://github.com/robotframework/robotframework/issues?q=milestone%3Av7.2
+.. _issue tracker: https://github.com/robotframework/robotframework/issues
+.. _robotframework-users: http://groups.google.com/group/robotframework-users
+.. _Slack: http://slack.robotframework.org
+.. _Robot Framework Slack: Slack_
+.. _installation instructions: ../../INSTALL.rst
+
+.. contents::
+ :depth: 2
+ :local:
+
+Most important enhancements
+===========================
+
+JSON output format
+------------------
+
+Robot Framework creates an output file during execution. The output file is
+needed when the log and the report are generated after the execution, and
+various external tools also use it to be able to show detailed execution
+information.
+
+The output file format has traditionally been XML, but Robot Framework 7.2
+supports also JSON output files (`#3423`_). The format is detected automatically
+based on the output file extension::
+
+ robot --output output.json example.robot
+
+If JSON output files are needed with earlier Robot Framework versions, it is
+possible to use the Rebot tool that got support to generate JSON output files
+already in `Robot Framework 7.0`__::
+
+ rebot --output output.json output.xml
+
+The format produced by the Rebot tool has changed in Robot Framework 7.2,
+though, so possible tools already using JSON outputs need to be updated (`#5160`_).
+The motivation for the change was adding statistics and execution errors also
+to the JSON output to make it compatible with the XML output.
+
+JSON output files created during execution and generated by Rebot use the same
+format. To learn more about the format, see its `schema definition`__.
+
+__ https://github.com/robotframework/robotframework/blob/master/doc/releasenotes/rf-7.0.rst#json-result-format
+__ https://github.com/robotframework/robotframework/tree/master/doc/schema#readme
+
+`GROUP` syntax
+--------------
+
+The new `GROUP` syntax (`#5257`_) allows grouping related keywords and control
+structures together:
+
+.. sourcecode:: robotframework
+
+ *** Test Cases ***
+ Valid login
+ GROUP Open browser to login page
+ Open Browser ${LOGIN URL}
+ Title Should Be Login Page
+ END
+ GROUP Submit credentials
+ Input Username username_field demo
+ Input Password password_field mode
+ Click Button login_button
+ END
+ GROUP Login should have succeeded
+ Title Should Be Welcome Page
+ END
+
+ Anonymous group
+ GROUP
+ Log Group name is optional.
+ END
+
+ Nesting
+ GROUP
+ GROUP Nested group
+ Log Groups can be nested.
+ END
+ IF True
+ GROUP
+ Log Groups can also be nested with other control structures.
+ END
+ END
+ END
+
+As the above examples demonstrates, groups can have a name, but the name is
+optional. Groups can also be nested freely with each others and with other
+control structures.
+
+User keywords are in general recommended over the `GROUP` syntax, because
+they are reusable and because they simplify tests or keywords where they are
+used by hiding lower level details. In the log file user keywords and groups
+look the same, though, except that there is a `GROUP` label instead of
+a `KEYWORD` label.
+
+All groups within a test or a keyword share the same variable namespace.
+This means that, unlike when using keywords, there is no need to use arguments
+or return values for sharing values. This can be a benefit in simple cases,
+but if there are lot of variables, the benefit can turn into a problem and
+cause a huge mess.
+
+`GROUP` with templates
+~~~~~~~~~~~~~~~~~~~~~~
+
+The `GROUP` syntax can be used for grouping iterations with test templates:
+
+.. sourcecode:: robotframework
+
+ *** Settings ***
+ Library String
+ Test Template Upper case should be
+
+ *** Test Cases ***
+ Template example
+ GROUP ASCII characters
+ a A
+ z Z
+ END
+ GROUP Latin-1 characters
+ ä Ä
+ ß SS
+ END
+ GROUP Numbers
+ 1 1
+ 9 9
+ END
+
+ *** Keywords ***
+ Upper case should be
+ [Arguments] ${char} ${expected}
+ ${actual} = Convert To Upper Case ${char}
+ Should Be Equal ${actual} ${expected}
+
+Programmatic usage
+~~~~~~~~~~~~~~~~~~
+
+One of the primary usages for groups is making it possible to create structured
+tests, tasks and keywords programmatically. For example, the following pre-run
+modifier adds a group with two keywords at the end of each modified test. Groups
+can be added also by listeners that use the listener API version 3.
+
+.. sourcecode:: python
+
+ from robot.api import SuiteVisitor
+
+
+ class GroupAdder(SuiteVisitor):
+
+ def start_test(self, test):
+ group = test.body.create_group(name='Example')
+ group.body.create_keyword(name='Log', args=['Hello, world!'])
+ group.body.create_keyword(name='No Operation')
+
+Enhancements for working with bytes
+-----------------------------------
+
+Bytes and binary data are used extensively in some domains. Working with them
+has been enhanced in various ways:
+
+- String representation of bytes outside the ASCII range has been fixed (`#5052`_).
+ This affects, for example, logging bytes and embedding bytes to strings in
+ arguments like `Header: ${value_in_bytes}`. A major benefit of the fix is that
+ the resulting string can be converted back to bytes using, for example, automatic
+ argument conversion.
+
+- Concatenating variables containing bytes yields bytes (`#5259`_). For example,
+ something like `${x}${y}${z}` is bytes if all variables are bytes. If any variable
+ is not bytes or there is anything else than variables, the resulting value is
+ a string.
+
+- The `Should Be Equal` keyword got support for argument conversion (`#5053`_) that
+ also works with bytes. For example,
+ `Should Be Equal ${value} RF type=bytes` validates that
+ `${value}` is equal to `b'RF'`.
+
+New Libdoc technology
+---------------------
+
+The Libdoc tools is used for generating documentation for libraries and resource
+files. It can generate spec files in XML and JSON formats for editors and other
+tools, but its most important usage is generating HTML documentation for humans.
+
+Libdoc's HTML outputs have been totally rewritten using a new technology (`#4304`_).
+The motivation was to move forward from jQuery templates that are not anymore
+maintained and to have a better base to develop HTML outputs forward in general.
+The plan is to use the same technology with Robot's log and report files in the
+future.
+
+The idea was not to change existing functionality in this release to make it
+easier to compare results created with old and new Libdoc versions. An exception
+to this rule was that Libdoc's HTML user interface got localization support (`#3676`_).
+Robot Framework 7.2 contains Libdoc translations for Finnish, French, Dutch and
+Portuguese in addition to English. New translations can be added, and existing
+enhanced, in the future releases. Instructions how to do that can be found
+here__ and you can ask help on the `#devel` channel on our Slack_ if needed.
+
+__ https://github.com/robotframework/robotframework/tree/master/src/web#readme
+
+Other major enhancements and fixes
+----------------------------------
+
+- As already mentioned when discussing enhancements to working with bytes,
+ the `Should Be Equal` keyword got support for argument conversion (`#5053`_).
+ It is not limited to bytes, but supports anything Robot's automatic argument
+ conversion supports like lists and dictionaries, decimal numbers, dates and so on.
+
+- Logging APIs now work if Robot Framework is run on a thread (`#5255`_).
+
+- A class decorated with the `@library` decorator is recognized as a library
+ regardless does its name match the module name or not (`#4959`_).
+
+- Logged messages are added to the result model that is build during execution
+ (`#5260`_). The biggest benefit is that messages are now available to listeners
+ inspecting the model.
+
+Backwards incompatible changes
+==============================
+
+We try to avoid backwards incompatible changes in general and limit bigger
+changes to major releases. There are, however, some backwards incompatible
+changes in this release, but they should affect only very few users.
+
+Listeners are notified about actions they initiate
+--------------------------------------------------
+
+Earlier if a listener executed a keyword using `BuiltIn.run_keyword` or logged
+something, listeners were not notified about these events. This meant that
+listeners could not react to all actions that occurred during execution and
+that the model build during execution did not match information listeners got.
+
+The aforementioned problem has now been fixed and listeners are notified about
+all keywords and messages (`#5268`_). This should not typically cause problems,
+but there is a possibility for recursion if a listener does something
+after it gets a notification about an action it initiated itself.
+
+Messages logged by `start_test` and `end_test` listener methods are preserved
+-----------------------------------------------------------------------------
+
+Messages logged by `start_test` and `end_test` listeners methods using
+`robot.api.logger` used to be ignored, but nowadays they are preserved (`#5266`_).
+They are shown in the log file directly under the corresponding test and in
+the result model they are in `TestCase.body` along with keywords and control
+structures used by the test.
+
+Messages in `TestCase.body` can cause problems with tools processing results
+if they expect to see only keywords and control structures. This requires
+tools processing results to be updated.
+
+Showing these messages in the log file can add unnecessary noise. If that
+happens, listeners need to be configured to log less or to log using a level
+that is not visible by default.
+
+Change to handling SKIP with templates
+--------------------------------------
+
+Earlier when a test with a template had multiple iterations and one of the
+iterations was skipped, the whole test was stopped and it got the SKIP status.
+Possible remaining iterations were not executed and possible earlier failures
+were ignored. This behavior was inconsistent compared to how failures are
+handled, because with them, all iterations are executed.
+
+Nowadays all iterations are executed even if one or more of them is skipped
+(`#4426`_). The aggregated result of a templated test with multiple iterations is:
+
+- FAIL if any of the iterations failed.
+- PASS if there were no failures and at least one iteration passed.
+- SKIP if all iterations were skipped.
+
+Changes to handling bytes
+-------------------------
+
+As discussed above, `working with bytes`__ has been enhanced so that string
+representation for bytes outside ASCII range has been fixed (`#5052`_) and
+concatenating variables containing bytes yields bytes (`#5259`_). Both of
+these are useful enhancements, but users depending on the old behavior need
+to update their tests or tasks.
+
+__ `Enhancements for working with bytes`_
+
+Other backwards incompatible changes
+------------------------------------
+
+- JSON output format produced by Rebot has changed (`#5160`_).
+- Source distribution format has been changed from `zip` to `tar.gz`. The reason
+ is that the Python source distributions format has been standardized to `tar.gz`
+ by `PEP 625 `__ and `zip` distributions are
+ deprecated (`#5296`_).
+- The `Message.html` attribute is serialized to JSON only if its value is `True`
+ (`#5216`_).
+- Module is not used as a library if it contains a class decorated with the
+ `@library` decorator (`#4959`_).
+
+Deprecated features
+===================
+
+Robot Framework 7.2 deprecates using a literal value like `-tag` for creating
+tags starting with a hyphen using the `Test Tags` setting (`#5252`_). In the
+future this syntax will be used for removing tags set in higher level suite
+initialization files, similarly as the `-tag` syntax can nowadays be used with
+the `[Tags]` setting. If tags starting with a hyphen are needed, it is possible
+to use the escaped format like `\-tag` to create them.
+
+Acknowledgements
+================
+
+
+Robot Framework development is sponsored by the `Robot Framework Foundation`_
+and its over 70 member organizations. If your organization is using Robot Framework
+and benefiting from it, consider joining the foundation to support its
+development as well.
+
+Robot Framework 7.2 team funded by the foundation consisted of `Pekka Klärck`_ and
+`Janne Härkönen `_. Janne worked only part-time and was
+mainly responsible on Libdoc enhancements. In addition to work done by them, the
+community has provided some great contributions:
+
+- Libdoc translations (`#3676`_) were provided by the following persons:
+
+ - Dutch by `Elout van Leeuwen `__ and
+ `J. Foederer `__
+ - French by `Gad Hassine `__
+ - Portuguese by `Hélio Guilherme `__
+
+- `René `__ provided a pull request to implement
+ the `GROUP` syntax (`#5257`_).
+
+- `Lajos Olah `__ enhanced how the SKIP status works
+ when using templates with multiple iterations (`#4426`_).
+
+- `Marcin Gmurczyk `__ made it possible to
+ ignore order in values when comparing dictionaries (`#5007`_).
+
+- `Mohd Maaz Usmani `__ added support to control
+ the separator when appending to an existing value using `Set Suite Metadata`,
+ `Set Test Documentation` and other such keywords (`#5215`_).
+
+- `Luis Carlos `__ made the public API of
+ the `robot.api.parsing` module explicit (`#5245`_).
+
+- `Theodore Georgomanolis `__ fixed `logging`
+ module usage so that the original log level is restored after execution (`#5262`_).
+
+- `Johnny.H `__ enhanced error message when using
+ the `Rebot` tool with an output file containing no tests or tasks (`#5312`_).
+
+Big thanks to Robot Framework Foundation, to community members listed above, and to
+everyone else who has tested preview releases, submitted bug reports, proposed
+enhancements, debugged problems, or otherwise helped with Robot Framework 7.2
+development.
+
+| `Pekka Klärck `_
+| Robot Framework lead developer
+
+Full list of fixes and enhancements
+===================================
+
+.. list-table::
+ :header-rows: 1
+
+ * - ID
+ - Type
+ - Priority
+ - Summary
+ * - `#3423`_
+ - enhancement
+ - critical
+ - Support JSON output files as part of execution
+ * - `#3676`_
+ - enhancement
+ - critical
+ - Libdoc localizations
+ * - `#4304`_
+ - enhancement
+ - critical
+ - New technology for Libdoc HTML outputs
+ * - `#5052`_
+ - bug
+ - high
+ - Invalid string representation for bytes outside ASCII range
+ * - `#5167`_
+ - bug
+ - high
+ - Crash if listener executes library keyword in `end_test` in the dry-run mode
+ * - `#5255`_
+ - bug
+ - high
+ - Logging APIs do not work if Robot Framework is run on thread
+ * - `#4959`_
+ - enhancement
+ - high
+ - Recognize library classes decorated with `@library` decorator regardless their name
+ * - `#5053`_
+ - enhancement
+ - high
+ - Support argument conversion with `Should Be Equal`
+ * - `#5160`_
+ - enhancement
+ - high
+ - Add execution errors and statistics to JSON output generated by Rebot
+ * - `#5257`_
+ - enhancement
+ - high
+ - `GROUP` syntax for grouping keywords and control structures
+ * - `#5260`_
+ - enhancement
+ - high
+ - Add log messages to result model that is build during execution and available to listeners
+ * - `#5170`_
+ - bug
+ - medium
+ - Failure in suite setup initiates exit-on-failure even if all tests have skip-on-failure active
+ * - `#5245`_
+ - bug
+ - medium
+ - `robot.api.parsing` doesn't have properly defined public API
+ * - `#5254`_
+ - bug
+ - medium
+ - Libdoc performance degradation starting from RF 6.0
+ * - `#5262`_
+ - bug
+ - medium
+ - `logging` module log level is not restored after execution
+ * - `#5266`_
+ - bug
+ - medium
+ - Messages logged by `start_test` and `end_test` listener methods are ignored
+ * - `#5268`_
+ - bug
+ - medium
+ - Listeners are not notified about actions they initiate
+ * - `#5269`_
+ - bug
+ - medium
+ - Recreating control structure results from JSON fails if they have messages mixed with iterations/branches
+ * - `#5274`_
+ - bug
+ - medium
+ - Problems with recommentation to use `$var` syntax if expression evaluation fails
+ * - `#5282`_
+ - bug
+ - medium
+ - `lineno` of keywords executed by `Run Keyword` variants is `None` in dry-run
+ * - `#5289`_
+ - bug
+ - medium
+ - Status of library keywords that are executed in dry-run is `NOT RUN`
+ * - `#4426`_
+ - enhancement
+ - medium
+ - All iterations of templated tests should be executed even if one is skipped
+ * - `#5007`_
+ - enhancement
+ - medium
+ - Collections: Support ignoring order in values when comparing dictionaries
+ * - `#5215`_
+ - enhancement
+ - medium
+ - Support controlling separator when appending current value using `Set Suite Metadata`, `Set Test Documentation` and other such keywords
+ * - `#5219`_
+ - enhancement
+ - medium
+ - Support stopping execution using `robot:exit-on-failure` tag
+ * - `#5223`_
+ - enhancement
+ - medium
+ - Allow setting variables with TEST scope in suite setup/teardown (not visible for tests or child suites)
+ * - `#5235`_
+ - enhancement
+ - medium
+ - Document that `Get Variable Value` and `Variable Should (Not) Exist` do not support named-argument syntax
+ * - `#5242`_
+ - enhancement
+ - medium
+ - Support inline flags for configuring custom embedded argument patterns
+ * - `#5251`_
+ - enhancement
+ - medium
+ - Allow listeners to remove log messages by setting them to `None`
+ * - `#5252`_
+ - enhancement
+ - medium
+ - Deprecate setting tags starting with a hyphen like `-tag` in `Test Tags`
+ * - `#5259`_
+ - enhancement
+ - medium
+ - Concatenating variables containing bytes should yield bytes
+ * - `#5264`_
+ - enhancement
+ - medium
+ - If test is skipped using `--skip` or `--skip-on-failure`, show used tags in test's message
+ * - `#5272`_
+ - enhancement
+ - medium
+ - Enhance recursion detection
+ * - `#5292`_
+ - enhancement
+ - medium
+ - `robot:skip` and `robot:exclude` tags do not support variables
+ * - `#5296`_
+ - enhancement
+ - medium
+ - Change source distribution format from deprecated `zip` to `tar.gz`
+ * - `#5202`_
+ - bug
+ - low
+ - Per-fle language configuration fails if there are two or more spaces after `Language:` prefix
+ * - `#5267`_
+ - bug
+ - low
+ - Message passed to `log_message` listener method has wrong type
+ * - `#5276`_
+ - bug
+ - low
+ - Templates should be explicitly prohibited with WHILE
+ * - `#5283`_
+ - bug
+ - low
+ - Documentation incorrectly claims that `--tagdoc` documentation supports HTML formatting
+ * - `#5288`_
+ - bug
+ - low
+ - `Message.id` broken if parent is not `Keyword` or `ExecutionErrors`
+ * - `#5295`_
+ - bug
+ - low
+ - Duplicate test name detection does not take variables into account
+ * - `#5309`_
+ - bug
+ - low
+ - Bug in `Return From Keyword If` documentation
+ * - `#5312`_
+ - bug
+ - low
+ - Confusing error message when using `rebot` and output file contains no tests
+ * - `#5155`_
+ - enhancement
+ - low
+ - Document where `log-.js` files created by `--splitlog` are saved
+ * - `#5216`_
+ - enhancement
+ - low
+ - Include `Message.html` in JSON results only if it is `True`
+ * - `#5238`_
+ - enhancement
+ - low
+ - Document return codes in `--help`
+ * - `#5286`_
+ - enhancement
+ - low
+ - Add suite and test `id` to JSON result model
+ * - `#5287`_
+ - enhancement
+ - low
+ - Add `type` attribute to `TestSuite` and `TestCase` objects
+
+Altogether 48 issues. View on the `issue tracker `__.
+
+.. _#3423: https://github.com/robotframework/robotframework/issues/3423
+.. _#3676: https://github.com/robotframework/robotframework/issues/3676
+.. _#4304: https://github.com/robotframework/robotframework/issues/4304
+.. _#5052: https://github.com/robotframework/robotframework/issues/5052
+.. _#5167: https://github.com/robotframework/robotframework/issues/5167
+.. _#5255: https://github.com/robotframework/robotframework/issues/5255
+.. _#4959: https://github.com/robotframework/robotframework/issues/4959
+.. _#5053: https://github.com/robotframework/robotframework/issues/5053
+.. _#5160: https://github.com/robotframework/robotframework/issues/5160
+.. _#5257: https://github.com/robotframework/robotframework/issues/5257
+.. _#5260: https://github.com/robotframework/robotframework/issues/5260
+.. _#5170: https://github.com/robotframework/robotframework/issues/5170
+.. _#5245: https://github.com/robotframework/robotframework/issues/5245
+.. _#5254: https://github.com/robotframework/robotframework/issues/5254
+.. _#5262: https://github.com/robotframework/robotframework/issues/5262
+.. _#5266: https://github.com/robotframework/robotframework/issues/5266
+.. _#5268: https://github.com/robotframework/robotframework/issues/5268
+.. _#5269: https://github.com/robotframework/robotframework/issues/5269
+.. _#5274: https://github.com/robotframework/robotframework/issues/5274
+.. _#5282: https://github.com/robotframework/robotframework/issues/5282
+.. _#5289: https://github.com/robotframework/robotframework/issues/5289
+.. _#4426: https://github.com/robotframework/robotframework/issues/4426
+.. _#5007: https://github.com/robotframework/robotframework/issues/5007
+.. _#5215: https://github.com/robotframework/robotframework/issues/5215
+.. _#5219: https://github.com/robotframework/robotframework/issues/5219
+.. _#5223: https://github.com/robotframework/robotframework/issues/5223
+.. _#5235: https://github.com/robotframework/robotframework/issues/5235
+.. _#5242: https://github.com/robotframework/robotframework/issues/5242
+.. _#5251: https://github.com/robotframework/robotframework/issues/5251
+.. _#5252: https://github.com/robotframework/robotframework/issues/5252
+.. _#5259: https://github.com/robotframework/robotframework/issues/5259
+.. _#5264: https://github.com/robotframework/robotframework/issues/5264
+.. _#5272: https://github.com/robotframework/robotframework/issues/5272
+.. _#5292: https://github.com/robotframework/robotframework/issues/5292
+.. _#5296: https://github.com/robotframework/robotframework/issues/5296
+.. _#5202: https://github.com/robotframework/robotframework/issues/5202
+.. _#5267: https://github.com/robotframework/robotframework/issues/5267
+.. _#5276: https://github.com/robotframework/robotframework/issues/5276
+.. _#5283: https://github.com/robotframework/robotframework/issues/5283
+.. _#5288: https://github.com/robotframework/robotframework/issues/5288
+.. _#5295: https://github.com/robotframework/robotframework/issues/5295
+.. _#5309: https://github.com/robotframework/robotframework/issues/5309
+.. _#5312: https://github.com/robotframework/robotframework/issues/5312
+.. _#5155: https://github.com/robotframework/robotframework/issues/5155
+.. _#5216: https://github.com/robotframework/robotframework/issues/5216
+.. _#5238: https://github.com/robotframework/robotframework/issues/5238
+.. _#5286: https://github.com/robotframework/robotframework/issues/5286
+.. _#5287: https://github.com/robotframework/robotframework/issues/5287
diff --git a/doc/releasenotes/rf-7.2b1.rst b/doc/releasenotes/rf-7.2b1.rst
new file mode 100644
index 00000000000..b65e395d7c1
--- /dev/null
+++ b/doc/releasenotes/rf-7.2b1.rst
@@ -0,0 +1,650 @@
+==========================
+Robot Framework 7.2 beta 1
+==========================
+
+.. default-role:: code
+
+`Robot Framework`_ 7.2 is a feature release with JSON output support (`#3423`_),
+`GROUP` syntax for grouping keywords and control structures (`#5257`_), new
+Libdoc technology (`#4304`_) including translations (`#3676`_), and various
+other features. This beta release contains most of the planned features, but
+some changes are still possible before the release candidate.
+
+All issues targeted for Robot Framework v7.2 can be found
+from the `issue tracker milestone`_.
+
+Questions and comments related to the release can be sent to the `#devel`
+channel on `Robot Framework Slack`_ and possible bugs submitted to
+the `issue tracker`_.
+
+If you have pip_ installed, just run
+
+::
+
+ pip install --pre --upgrade robotframework
+
+to install the latest available release or use
+
+::
+
+ pip install robotframework==7.2b1
+
+to install exactly this version. Alternatively you can download the package
+from PyPI_ and install it manually. For more details and other installation
+approaches, see the `installation instructions`_.
+
+Robot Framework 7.2 beta 1 was released on Wednesday December 18, 2024.
+The first release candidate is planned to be released in the first days of
+2025 and the final release two weeks from that.
+
+.. _Robot Framework: http://robotframework.org
+.. _Robot Framework Foundation: http://robotframework.org/foundation
+.. _pip: http://pip-installer.org
+.. _PyPI: https://pypi.python.org/pypi/robotframework
+.. _issue tracker milestone: https://github.com/robotframework/robotframework/issues?q=milestone%3Av7.2
+.. _issue tracker: https://github.com/robotframework/robotframework/issues
+.. _robotframework-users: http://groups.google.com/group/robotframework-users
+.. _Slack: http://slack.robotframework.org
+.. _Robot Framework Slack: Slack_
+.. _installation instructions: ../../INSTALL.rst
+
+.. contents::
+ :depth: 2
+ :local:
+
+Most important enhancements
+===========================
+
+JSON output format
+------------------
+
+Robot Framework creates an output file during execution. The output file is
+needed when the log and the report are generated after the execution and
+various external tools also use it to be able to show detailed execution
+information.
+
+The output file format has traditionally been XML, but Robot Framework 7.2
+supports also JSON output files (`#3423`_). The format is detected automatically
+based on the output file extension::
+
+ robot --output output.json example.robot
+
+If JSON output files are needed with earlier Robot Framework versions, it is
+possible to use the Rebot tool that got support to generate JSON output files
+already in `Robot Framework 7.0`__::
+
+ rebot --output output.json output.xml
+
+The format produced by the Rebot tool has changed in Robot Framework 7.2,
+though, so possible tools already using JSON outputs need to be updated.
+The motivation for the change was adding statistics and execution errors also
+to the JSON output to make it compatible with the XML output (`#5160`_).
+
+JSON output files created during execution and generated by Rebot use the same
+format. To learn more about the format, see its `schema definition`__.
+
+__ https://github.com/robotframework/robotframework/blob/master/doc/releasenotes/rf-7.0.rst#json-result-format
+__ https://github.com/robotframework/robotframework/tree/master/doc/schema#readme
+
+`GROUP` syntax
+--------------
+
+The new `GROUP` syntax (`#5257`_) allows grouping related keywords and control
+structures together:
+
+.. sourcecode:: robotframework
+
+ *** Test Cases ***
+ Valid login
+ GROUP Open browser to login page
+ Open Browser ${LOGIN URL}
+ Title Should Be Login Page
+ END
+ GROUP Submit credentials
+ Input Username username_field demo
+ Input Password password_field mode
+ Click Button login_button
+ END
+ GROUP Login should have succeeded
+ Title Should Be Welcome Page
+ END
+
+ Anonymous group
+ GROUP
+ Log Group name is optional.
+ END
+
+ Nesting
+ GROUP
+ GROUP Nested group
+ Log Groups can be nested.
+ END
+ IF True
+ GROUP
+ Log Groups can also be nested with other control structures.
+ END
+ END
+ END
+
+As the above examples demonstrates, groups can have a name, but the name is
+optional. Groups can also be nested freely with each others and with other
+control structures.
+
+User keywords are in general recommended over the `GROUP` syntax, because
+they are reusable and because they simplify tests or keywords where they are
+used by hiding and encapsulating lower level details. In the log file user
+keywords and groups look the same, though, except that instead of a `KEYWORD`
+label there is a `GROUP` label.
+
+All groups within a test or a keyword share the same variable namespace.
+This means that, unlike when using keywords, there is no need to use arguments
+or return values for sharing values. This can be a benefit in simple cases,
+but if there are lot of variables, the benefit can turn into a problem and
+cause a huge mess.
+
+`GROUP` with templates
+~~~~~~~~~~~~~~~~~~~~~~
+
+The `GROUP` syntax can be used for grouping iterations with test templates:
+
+.. sourcecode:: robotframework
+
+ *** Settings ***
+ Library String
+ Test Template Upper case should be
+
+ *** Test Cases ***
+ Template example
+ GROUP ASCII characters
+ a A
+ z Z
+ END
+ GROUP Latin-1 characters
+ ä Ä
+ ß SS
+ END
+ GROUP Numbers
+ 1 1
+ 9 9
+ END
+
+ *** Keywords ***
+ Upper case should be
+ [Arguments] ${char} ${expected}
+ ${actual} = Convert To Upper Case ${char}
+ Should Be Equal ${actual} ${expected}
+
+Programmatic usage
+~~~~~~~~~~~~~~~~~~
+
+One of the primary usages for groups is making it possible to create structured
+tests, tasks and keywords programmatically. For example, the following pre-run
+modifier adds a group with two keywords at the end of each modified test. Groups
+can be added also by listeners that use the listener API version 3.
+
+.. sourcecode:: python
+
+ from robot.api import SuiteVisitor
+
+
+ class GroupAdder(SuiteVisitor):
+
+ def start_test(self, test):
+ group = test.body.create_group(name='Example')
+ group.body.create_keyword(name='Log', args=['Hello, world!'])
+ group.body.create_keyword(name='No Operation')
+
+Enhancements for working with bytes
+-----------------------------------
+
+Bytes and binary data are used extensively in some domains. Working with them
+has been enhanced in various ways:
+
+- String representation of bytes outside the ASCII range has been fixed (`#5052`_).
+ This affects, for example, logging bytes and embedding bytes to strings in
+ arguments like `Header: ${value_in_bytes}`. A major benefit of the fix is that
+ the resulting string can be converted back to bytes using, for example, automatic
+ argument conversion.
+
+- Concatenating variables containing bytes yields bytes (`#5259`_). For example,
+ something like `${x}${y}${z}` is bytes if all variables are bytes. If any variable
+ is not bytes or there is anything else than variables, the resulting value is
+ a string.
+
+- The `Should Be Equal` keyword got support for argument conversion (`#5053`_) that
+ also works with bytes. For example,
+ `Should Be Equal ${value} RF type=bytes` validates that
+ `${value}` is is equal to `b'RF'`.
+
+New Libdoc technology
+---------------------
+
+The Libdoc tools is used for generating documentation for libraries and resource
+files. It can generate spec files in XML and JSON formats for editors and other
+tools, but its most important usage is generating HTML documentation for humans.
+
+Libdoc's HTML outputs have been totally rewritten using a new technology (`#4304`_).
+The motivation was to move forward from jQuery templates that are not anymore
+maintained and to have a better base to develop HTML outputs forward in general.
+The plan is to use the same technology with Robot's log and report files in the
+future.
+
+The idea was not to change existing functionality in this release to make it
+easier to compare results created with old and new Libdoc versions. An exception
+to this rule is that Libdoc's HTML user interface can be localized (`#3676`_).
+If you would like Libdoc to support your native language, there is still time
+to add localizations before the final release! If you are interested, see
+the instructions__ and ask help on the `#devel` channel on our Slack_ if needed.
+
+We hope that library developers test the new Libdoc with their libraries and
+report possible problems so that we can fix them before the final release.
+
+__ https://github.com/robotframework/robotframework/tree/master/src/web#readme
+
+Other major enhancements and fixes
+----------------------------------
+
+- As already mentioned when discussing enhancements to working with bytes,
+ the `Should Be Equal` keyword got support for argument conversion (`#5053`_).
+ It is not limited to bytes, but supports anything Robot's automatic argument
+ conversion supports like lists and dictionaries, decimal numbers, dates and so on.
+
+- Logging APIs now work if Robot Framework is run on thread (`#5255`_).
+
+- Classes decorated with the `@library` decorator are recognized as libraries
+ regardless do their name match the module name (`#4959`_).
+
+- Logged messages are added to the result model that is build during execution
+ (`#5260`_). The biggest benefit is that messages are now available to listeners
+ inspecting the model.
+
+Backwards incompatible changes
+==============================
+
+We try to avoid backwards incompatible changes in general and limit bigger
+changes to major releases. There are, however, some backwards incompatible
+changes in this release, but they should affect only very few users.
+
+Listeners are notified about actions they initiate
+--------------------------------------------------
+
+Earlier if a listener executed a keyword using `BuiltIn.run_keyword` or logged
+something, listeners were not notified about these events. This meant that
+listeners could not react to all actions that occurred during execution and
+that the model build during execution did not match information listeners got.
+
+The aforementioned problem has now been fixed and listeners are notified about
+all keywords and messages (`#5268`_). This should not typically cause problems,
+but there is a possibility for recursion if a listener does something
+after it gets a notification about an action it initiated. Luckily detecting
+recursion in listeners themselves is fairly easy.
+
+Change to handling SKIP with templates
+--------------------------------------
+
+Earlier when a templated test had multiple iterations and one of the iterations
+was skipped, the test was stopped and it got the SKIP status. Possible remaining
+iterations were not executed and possible earlier failures were ignored.
+This behavior was inconsistent compared to how failures are handled, because
+if there are failures, all iterations are executed anyway.
+
+Nowadays all iterations are executed even if one or more of them is skipped
+(`#4426`_). The aggregated result of a templated test with multiple iterations is:
+
+- FAIL if any of the iterations failed.
+- PASS if there were no failures and at least one iteration passed.
+- SKIP if all iterations were skipped.
+
+Changes to handling bytes
+-------------------------
+
+As discussed above, `working with bytes`__ has been enhanced so that
+string representation for bytes outside ASCII range has been fixed (`#5052`_)
+and concatenating variables containing bytes yields bytes (`#5259`_).
+Both of these are useful enhancements, but users depending on the old
+behavior need to update their tests or tasks.
+
+__ `Enhancements for working with bytes`_
+
+Other backwards incompatible changes
+------------------------------------
+
+- JSON output format produced by Rebot has changed (`#5160`_).
+- Module is not used as a library if it contains a class decorated with the
+ `@library` decorator (`#4959`_).
+- Messages in JSON results have `html` attribute only if it is `True` (`#5216`_).
+
+Deprecated features
+===================
+
+Robot Framework 7.2 deprecates using a literal value like `-tag` for creating
+tags starting with a hyphen using the `Test Tags` setting (`#5252`_). In the
+future this syntax will be used for removing tags set in higher level suite
+initialization files, similarly as the `-tag` syntax can nowadays be used with
+the `[Tags]` setting. If tags starting with a hyphen are needed, it is possible
+to use the escaped format like `\-tag` to create them.
+
+Acknowledgements
+================
+
+Robot Framework development is sponsored by the `Robot Framework Foundation`_
+and its over 60 member organizations. If your organization is using Robot Framework
+and benefiting from it, consider joining the foundation to support its
+development as well.
+
+Robot Framework 7.0 team funded by the foundation consisted of `Pekka Klärck`_ and
+`Janne Härkönen `_. Janne worked only part-time and was
+mainly responsible on Libdoc enhancements. In addition to work done by them, the
+community has provided some great contributions:
+
+- `René `__ provided a pull request to implement
+ the `GROUP` syntax (`#5257`_).
+
+- `Lajos Olah `__ enhanced how the SKIP status works
+ when using templates with multiple iterations (`#4426`_).
+
+- `Marcin Gmurczyk `__ made it possible to
+ ignore order in values when comparing dictionaries (`#5007`_).
+
+- `Mohd Maaz Usmani `__ added support to control
+ the separator when appending to an existing value using `Set Suite Metadata`,
+ `Set Test Documentation` and other such keywords (`#5215`_).
+
+- `Luis Carlos `__ added explicit public API
+ to the `robot.api.parsing` module (`#5245`_).
+
+- `Theodore Georgomanolis `__ fixed `logging`
+ module usage so that the original log level is restored after execution (`#5262`_).
+
+Big thanks to Robot Framework Foundation, to community members listed above, and to
+everyone else who has tested preview releases, submitted bug reports, proposed
+enhancements, debugged problems, or otherwise helped with Robot Framework 7.2
+development.
+
+| `Pekka Klärck `_
+| Robot Framework lead developer
+
+Full list of fixes and enhancements
+===================================
+
+.. list-table::
+ :header-rows: 1
+
+ * - ID
+ - Type
+ - Priority
+ - Summary
+ - Added
+ * - `#3423`_
+ - enhancement
+ - critical
+ - Support JSON output files as part of execution
+ - beta 1
+ * - `#3676`_
+ - enhancement
+ - critical
+ - Libdoc localizations
+ - beta 1
+ * - `#4304`_
+ - enhancement
+ - critical
+ - New technology for Libdoc HTML outputs
+ - beta 1
+ * - `#5052`_
+ - bug
+ - high
+ - Invalid string representation for bytes outside ASCII range
+ - beta 1
+ * - `#5167`_
+ - bug
+ - high
+ - Crash if listener executes library keyword in `end_test` in the dry-run mode
+ - beta 1
+ * - `#5255`_
+ - bug
+ - high
+ - Logging APIs do not work if Robot Framework is run on thread
+ - beta 1
+ * - `#4959`_
+ - enhancement
+ - high
+ - Recognize library classes decorated with `@library` decorator regardless their name
+ - beta 1
+ * - `#5053`_
+ - enhancement
+ - high
+ - Support argument conversion with `Should Be Equal`
+ - beta 1
+ * - `#5160`_
+ - enhancement
+ - high
+ - Add execution errors and statistics to JSON output generated by Rebot
+ - beta 1
+ * - `#5257`_
+ - enhancement
+ - high
+ - `GROUP` syntax for grouping keywords and control structures
+ - beta 1
+ * - `#5260`_
+ - enhancement
+ - high
+ - Add log messages to result model that is build during execution and available to listeners
+ - beta 1
+ * - `#5170`_
+ - bug
+ - medium
+ - Failure in suite setup initiates exit-on-failure even if all tests have skip-on-failure active
+ - beta 1
+ * - `#5245`_
+ - bug
+ - medium
+ - `robot.api.parsing` doesn't have properly defined public API
+ - beta 1
+ * - `#5254`_
+ - bug
+ - medium
+ - Libdoc performance degradation starting from RF 6.0
+ - beta 1
+ * - `#5262`_
+ - bug
+ - medium
+ - `logging` module log level is not restored after execution
+ - beta 1
+ * - `#5266`_
+ - bug
+ - medium
+ - Messages logged by `start_test` and `end_test` listener methods are ignored
+ - beta 1
+ * - `#5268`_
+ - bug
+ - medium
+ - Listeners are not notified about actions they initiate
+ - beta 1
+ * - `#5269`_
+ - bug
+ - medium
+ - Recreating control structure results from JSON fails if they have messages mixed with iterations/branches
+ - beta 1
+ * - `#5274`_
+ - bug
+ - medium
+ - Problems with recommentation to use `$var` syntax if expression evaluation fails
+ - beta 1
+ * - `#5282`_
+ - bug
+ - medium
+ - `lineno` of keywords executed by `Run Keyword` variants is `None` in dry-run
+ - beta 1
+ * - `#5289`_
+ - bug
+ - medium
+ - Status of library keywords that are executed in dry-run is `NOT RUN`
+ - beta 1
+ * - `#4426`_
+ - enhancement
+ - medium
+ - All iterations of templated tests should be executed even if one is skipped
+ - beta 1
+ * - `#5007`_
+ - enhancement
+ - medium
+ - Collections: Support ignoring order in values when comparing dictionaries
+ - beta 1
+ * - `#5219`_
+ - enhancement
+ - medium
+ - Support stopping execution using `robot:exit-on-failure` tag
+ - beta 1
+ * - `#5223`_
+ - enhancement
+ - medium
+ - Allow setting variables with TEST scope in suite setup/teardown (not visible for tests or child suites)
+ - beta 1
+ * - `#5235`_
+ - enhancement
+ - medium
+ - Document that `Get Variable Value` and `Variable Should (Not) Exist` do not support named-argument syntax
+ - beta 1
+ * - `#5242`_
+ - enhancement
+ - medium
+ - Support inline flags for configuring custom embedded argument patterns
+ - beta 1
+ * - `#5251`_
+ - enhancement
+ - medium
+ - Allow listeners to remove log messages by setting them to `None`
+ - beta 1
+ * - `#5252`_
+ - enhancement
+ - medium
+ - Deprecate setting tags starting with a hyphen like `-tag` in `Test Tags`
+ - beta 1
+ * - `#5259`_
+ - enhancement
+ - medium
+ - Concatenating variables containing bytes should yield bytes
+ - beta 1
+ * - `#5264`_
+ - enhancement
+ - medium
+ - If test is skipped using `--skip` or `--skip-on-failure`, show used tags in test's message
+ - beta 1
+ * - `#5272`_
+ - enhancement
+ - medium
+ - Enhance recursion detection
+ - beta 1
+ * - `#5292`_
+ - enhancement
+ - medium
+ - `robot:skip` and `robot:exclude` tags do not support variables
+ - beta 1
+ * - `#5202`_
+ - bug
+ - low
+ - Per-fle language configuration fails if there are two or more spaces after `Language:` prefix
+ - beta 1
+ * - `#5267`_
+ - bug
+ - low
+ - Message passed to `log_message` listener method has wrong type
+ - beta 1
+ * - `#5276`_
+ - bug
+ - low
+ - Templates should be explicitly prohibited with WHILE
+ - beta 1
+ * - `#5283`_
+ - bug
+ - low
+ - Documentation incorrectly claims that `--tagdoc` documentation supports HTML formatting
+ - beta 1
+ * - `#5288`_
+ - bug
+ - low
+ - `Message.id` broken if parent is not `Keyword` or `ExecutionErrors`
+ - beta 1
+ * - `#5295`_
+ - bug
+ - low
+ - Duplicate test name detection does not take variables into account
+ - beta 1
+ * - `#5155`_
+ - enhancement
+ - low
+ - Document where `log-.js` files created by `--splitlog` are saved
+ - beta 1
+ * - `#5215`_
+ - enhancement
+ - low
+ - Support controlling separator when appending current value using `Set Suite Metadata`, `Set Test Documentation` and other such keywords
+ - beta 1
+ * - `#5216`_
+ - enhancement
+ - low
+ - Include `Message.html` in JSON results only if it is `True`
+ - beta 1
+ * - `#5238`_
+ - enhancement
+ - low
+ - Document return codes in `--help`
+ - beta 1
+ * - `#5286`_
+ - enhancement
+ - low
+ - Add suite and test `id` to JSON result model
+ - beta 1
+ * - `#5287`_
+ - enhancement
+ - low
+ - Add `type` attribute to `TestSuite` and `TestCase` objects
+ - beta 1
+
+Altogether 45 issues. View on the `issue tracker `__.
+
+.. _#3423: https://github.com/robotframework/robotframework/issues/3423
+.. _#3676: https://github.com/robotframework/robotframework/issues/3676
+.. _#4304: https://github.com/robotframework/robotframework/issues/4304
+.. _#5052: https://github.com/robotframework/robotframework/issues/5052
+.. _#5167: https://github.com/robotframework/robotframework/issues/5167
+.. _#5255: https://github.com/robotframework/robotframework/issues/5255
+.. _#4959: https://github.com/robotframework/robotframework/issues/4959
+.. _#5053: https://github.com/robotframework/robotframework/issues/5053
+.. _#5160: https://github.com/robotframework/robotframework/issues/5160
+.. _#5257: https://github.com/robotframework/robotframework/issues/5257
+.. _#5260: https://github.com/robotframework/robotframework/issues/5260
+.. _#5170: https://github.com/robotframework/robotframework/issues/5170
+.. _#5245: https://github.com/robotframework/robotframework/issues/5245
+.. _#5254: https://github.com/robotframework/robotframework/issues/5254
+.. _#5262: https://github.com/robotframework/robotframework/issues/5262
+.. _#5266: https://github.com/robotframework/robotframework/issues/5266
+.. _#5268: https://github.com/robotframework/robotframework/issues/5268
+.. _#5269: https://github.com/robotframework/robotframework/issues/5269
+.. _#5274: https://github.com/robotframework/robotframework/issues/5274
+.. _#5282: https://github.com/robotframework/robotframework/issues/5282
+.. _#5289: https://github.com/robotframework/robotframework/issues/5289
+.. _#4426: https://github.com/robotframework/robotframework/issues/4426
+.. _#5007: https://github.com/robotframework/robotframework/issues/5007
+.. _#5219: https://github.com/robotframework/robotframework/issues/5219
+.. _#5223: https://github.com/robotframework/robotframework/issues/5223
+.. _#5235: https://github.com/robotframework/robotframework/issues/5235
+.. _#5242: https://github.com/robotframework/robotframework/issues/5242
+.. _#5251: https://github.com/robotframework/robotframework/issues/5251
+.. _#5252: https://github.com/robotframework/robotframework/issues/5252
+.. _#5259: https://github.com/robotframework/robotframework/issues/5259
+.. _#5264: https://github.com/robotframework/robotframework/issues/5264
+.. _#5272: https://github.com/robotframework/robotframework/issues/5272
+.. _#5292: https://github.com/robotframework/robotframework/issues/5292
+.. _#5202: https://github.com/robotframework/robotframework/issues/5202
+.. _#5267: https://github.com/robotframework/robotframework/issues/5267
+.. _#5276: https://github.com/robotframework/robotframework/issues/5276
+.. _#5283: https://github.com/robotframework/robotframework/issues/5283
+.. _#5288: https://github.com/robotframework/robotframework/issues/5288
+.. _#5295: https://github.com/robotframework/robotframework/issues/5295
+.. _#5155: https://github.com/robotframework/robotframework/issues/5155
+.. _#5215: https://github.com/robotframework/robotframework/issues/5215
+.. _#5216: https://github.com/robotframework/robotframework/issues/5216
+.. _#5238: https://github.com/robotframework/robotframework/issues/5238
+.. _#5286: https://github.com/robotframework/robotframework/issues/5286
+.. _#5287: https://github.com/robotframework/robotframework/issues/5287
diff --git a/doc/releasenotes/rf-7.2rc1.rst b/doc/releasenotes/rf-7.2rc1.rst
new file mode 100644
index 00000000000..8e7941c5df9
--- /dev/null
+++ b/doc/releasenotes/rf-7.2rc1.rst
@@ -0,0 +1,655 @@
+=======================================
+Robot Framework 7.2 release candidate 1
+=======================================
+
+.. default-role:: code
+
+`Robot Framework`_ 7.2 is a feature release with JSON output support (`#3423`_),
+`GROUP` syntax for grouping keywords and control structures (`#5257`_), new
+Libdoc technology (`#4304`_) including translations (`#3676`_), and various
+other features. This release candidate contains all planned changes, but new
+Libdoc translations can still be added before the final release and possible
+bugs will be fixed.
+
+Questions and comments related to the release can be sent to the `#devel`
+channel on `Robot Framework Slack`_ and possible bugs submitted to
+the `issue tracker`_.
+
+If you have pip_ installed, just run
+
+::
+
+ pip install --pre --upgrade robotframework
+
+to install the latest available release or use
+
+::
+
+ pip install robotframework==7.2rc1
+
+to install exactly this version. Alternatively you can download the package
+from PyPI_ and install it manually. For more details and other installation
+approaches, see the `installation instructions`_.
+
+Robot Framework 7.2 release candidate 1 was released on Tuesday December 31, 2024.
+The final release is targeted for Tuesday January 14, 2025.
+
+.. _Robot Framework: http://robotframework.org
+.. _Robot Framework Foundation: http://robotframework.org/foundation
+.. _pip: http://pip-installer.org
+.. _PyPI: https://pypi.python.org/pypi/robotframework
+.. _issue tracker milestone: https://github.com/robotframework/robotframework/issues?q=milestone%3Av7.2
+.. _issue tracker: https://github.com/robotframework/robotframework/issues
+.. _robotframework-users: http://groups.google.com/group/robotframework-users
+.. _Slack: http://slack.robotframework.org
+.. _Robot Framework Slack: Slack_
+.. _installation instructions: ../../INSTALL.rst
+
+.. contents::
+ :depth: 2
+ :local:
+
+Most important enhancements
+===========================
+
+JSON output format
+------------------
+
+Robot Framework creates an output file during execution. The output file is
+needed when the log and the report are generated after the execution, and
+various external tools also use it to be able to show detailed execution
+information.
+
+The output file format has traditionally been XML, but Robot Framework 7.2
+supports also JSON output files (`#3423`_). The format is detected automatically
+based on the output file extension::
+
+ robot --output output.json example.robot
+
+If JSON output files are needed with earlier Robot Framework versions, it is
+possible to use the Rebot tool that got support to generate JSON output files
+already in `Robot Framework 7.0`__::
+
+ rebot --output output.json output.xml
+
+The format produced by the Rebot tool has changed in Robot Framework 7.2,
+though, so possible tools already using JSON outputs need to be updated (`#5160`_).
+The motivation for the change was adding statistics and execution errors also
+to the JSON output to make it compatible with the XML output.
+
+JSON output files created during execution and generated by Rebot use the same
+format. To learn more about the format, see its `schema definition`__.
+
+__ https://github.com/robotframework/robotframework/blob/master/doc/releasenotes/rf-7.0.rst#json-result-format
+__ https://github.com/robotframework/robotframework/tree/master/doc/schema#readme
+
+`GROUP` syntax
+--------------
+
+The new `GROUP` syntax (`#5257`_) allows grouping related keywords and control
+structures together:
+
+.. sourcecode:: robotframework
+
+ *** Test Cases ***
+ Valid login
+ GROUP Open browser to login page
+ Open Browser ${LOGIN URL}
+ Title Should Be Login Page
+ END
+ GROUP Submit credentials
+ Input Username username_field demo
+ Input Password password_field mode
+ Click Button login_button
+ END
+ GROUP Login should have succeeded
+ Title Should Be Welcome Page
+ END
+
+ Anonymous group
+ GROUP
+ Log Group name is optional.
+ END
+
+ Nesting
+ GROUP
+ GROUP Nested group
+ Log Groups can be nested.
+ END
+ IF True
+ GROUP
+ Log Groups can also be nested with other control structures.
+ END
+ END
+ END
+
+As the above examples demonstrates, groups can have a name, but the name is
+optional. Groups can also be nested freely with each others and with other
+control structures.
+
+User keywords are in general recommended over the `GROUP` syntax, because
+they are reusable and because they simplify tests or keywords where they are
+used by hiding lower level details. In the log file user keywords and groups
+look the same, though, except that there is a `GROUP` label instead of
+a `KEYWORD` label.
+
+All groups within a test or a keyword share the same variable namespace.
+This means that, unlike when using keywords, there is no need to use arguments
+or return values for sharing values. This can be a benefit in simple cases,
+but if there are lot of variables, the benefit can turn into a problem and
+cause a huge mess.
+
+`GROUP` with templates
+~~~~~~~~~~~~~~~~~~~~~~
+
+The `GROUP` syntax can be used for grouping iterations with test templates:
+
+.. sourcecode:: robotframework
+
+ *** Settings ***
+ Library String
+ Test Template Upper case should be
+
+ *** Test Cases ***
+ Template example
+ GROUP ASCII characters
+ a A
+ z Z
+ END
+ GROUP Latin-1 characters
+ ä Ä
+ ß SS
+ END
+ GROUP Numbers
+ 1 1
+ 9 9
+ END
+
+ *** Keywords ***
+ Upper case should be
+ [Arguments] ${char} ${expected}
+ ${actual} = Convert To Upper Case ${char}
+ Should Be Equal ${actual} ${expected}
+
+Programmatic usage
+~~~~~~~~~~~~~~~~~~
+
+One of the primary usages for groups is making it possible to create structured
+tests, tasks and keywords programmatically. For example, the following pre-run
+modifier adds a group with two keywords at the end of each modified test. Groups
+can be added also by listeners that use the listener API version 3.
+
+.. sourcecode:: python
+
+ from robot.api import SuiteVisitor
+
+
+ class GroupAdder(SuiteVisitor):
+
+ def start_test(self, test):
+ group = test.body.create_group(name='Example')
+ group.body.create_keyword(name='Log', args=['Hello, world!'])
+ group.body.create_keyword(name='No Operation')
+
+Enhancements for working with bytes
+-----------------------------------
+
+Bytes and binary data are used extensively in some domains. Working with them
+has been enhanced in various ways:
+
+- String representation of bytes outside the ASCII range has been fixed (`#5052`_).
+ This affects, for example, logging bytes and embedding bytes to strings in
+ arguments like `Header: ${value_in_bytes}`. A major benefit of the fix is that
+ the resulting string can be converted back to bytes using, for example, automatic
+ argument conversion.
+
+- Concatenating variables containing bytes yields bytes (`#5259`_). For example,
+ something like `${x}${y}${z}` is bytes if all variables are bytes. If any variable
+ is not bytes or there is anything else than variables, the resulting value is
+ a string.
+
+- The `Should Be Equal` keyword got support for argument conversion (`#5053`_) that
+ also works with bytes. For example,
+ `Should Be Equal ${value} RF type=bytes` validates that
+ `${value}` is equal to `b'RF'`.
+
+New Libdoc technology
+---------------------
+
+The Libdoc tools is used for generating documentation for libraries and resource
+files. It can generate spec files in XML and JSON formats for editors and other
+tools, but its most important usage is generating HTML documentation for humans.
+
+Libdoc's HTML outputs have been totally rewritten using a new technology (`#4304`_).
+The motivation was to move forward from jQuery templates that are not anymore
+maintained and to have a better base to develop HTML outputs forward in general.
+The plan is to use the same technology with Robot's log and report files in the
+future.
+
+The idea was not to change existing functionality in this release to make it
+easier to compare results created with old and new Libdoc versions. An exception
+to this rule is that Libdoc's HTML user interface can be localized (`#3676`_).
+If you would like Libdoc to support your native language, there is still time
+to add localizations before the final release! If you are interested, see
+the instructions__ and ask help on the `#devel` channel on our Slack_ if needed.
+
+We hope that library developers test the new Libdoc with their libraries and
+report possible problems so that we can fix them before the final release.
+
+__ https://github.com/robotframework/robotframework/tree/master/src/web#readme
+
+Other major enhancements and fixes
+----------------------------------
+
+- As already mentioned when discussing enhancements to working with bytes,
+ the `Should Be Equal` keyword got support for argument conversion (`#5053`_).
+ It is not limited to bytes, but supports anything Robot's automatic argument
+ conversion supports like lists and dictionaries, decimal numbers, dates and so on.
+
+- Logging APIs now work if Robot Framework is run on a thread (`#5255`_).
+
+- A class decorated with the `@library` decorator is recognized as a library
+ regardless does its name match the module name or not (`#4959`_).
+
+- Logged messages are added to the result model that is build during execution
+ (`#5260`_). The biggest benefit is that messages are now available to listeners
+ inspecting the model.
+
+Backwards incompatible changes
+==============================
+
+We try to avoid backwards incompatible changes in general and limit bigger
+changes to major releases. There are, however, some backwards incompatible
+changes in this release, but they should affect only very few users.
+
+Listeners are notified about actions they initiate
+--------------------------------------------------
+
+Earlier if a listener executed a keyword using `BuiltIn.run_keyword` or logged
+something, listeners were not notified about these events. This meant that
+listeners could not react to all actions that occurred during execution and
+that the model build during execution did not match information listeners got.
+
+The aforementioned problem has now been fixed and listeners are notified about
+all keywords and messages (`#5268`_). This should not typically cause problems,
+but there is a possibility for recursion if a listener does something
+after it gets a notification about an action it initiated.
+
+Change to handling SKIP with templates
+--------------------------------------
+
+Earlier when a templated test had multiple iterations and one of the iterations
+was skipped, the test was stopped and it got the SKIP status. Possible remaining
+iterations were not executed and possible earlier failures were ignored.
+This behavior was inconsistent compared to how failures are handled, because
+if there are failures, all iterations are executed anyway.
+
+Nowadays all iterations are executed even if one or more of them is skipped
+(`#4426`_). The aggregated result of a templated test with multiple iterations is:
+
+- FAIL if any of the iterations failed.
+- PASS if there were no failures and at least one iteration passed.
+- SKIP if all iterations were skipped.
+
+Changes to handling bytes
+-------------------------
+
+As discussed above, `working with bytes`__ has been enhanced so that
+string representation for bytes outside ASCII range has been fixed (`#5052`_)
+and concatenating variables containing bytes yields bytes (`#5259`_).
+Both of these are useful enhancements, but users depending on the old
+behavior need to update their tests or tasks.
+
+__ `Enhancements for working with bytes`_
+
+Other backwards incompatible changes
+------------------------------------
+
+- JSON output format produced by Rebot has changed (`#5160`_).
+- Source distribution format has been changed from `zip` to `tag.gz`. The reason
+ is that the Python source distributions format has been standardized to `tar.gz`
+ by `PEP 625 `__ (`#5296`_).
+- Messages in JSON results have an `html` attribute only if its value is `True` (`#5216`_).
+- Module is not used as a library if it contains a class decorated with the
+ `@library` decorator (`#4959`_).
+
+Deprecated features
+===================
+
+Robot Framework 7.2 deprecates using a literal value like `-tag` for creating
+tags starting with a hyphen using the `Test Tags` setting (`#5252`_). In the
+future this syntax will be used for removing tags set in higher level suite
+initialization files, similarly as the `-tag` syntax can nowadays be used with
+the `[Tags]` setting. If tags starting with a hyphen are needed, it is possible
+to use the escaped format like `\-tag` to create them.
+
+Acknowledgements
+================
+
+Robot Framework development is sponsored by the `Robot Framework Foundation`_
+and its over 60 member organizations. If your organization is using Robot Framework
+and benefiting from it, consider joining the foundation to support its
+development as well.
+
+Robot Framework 7.0 team funded by the foundation consisted of `Pekka Klärck`_ and
+`Janne Härkönen `_. Janne worked only part-time and was
+mainly responsible on Libdoc enhancements. In addition to work done by them, the
+community has provided some great contributions:
+
+- `René `__ provided a pull request to implement
+ the `GROUP` syntax (`#5257`_).
+
+- `Lajos Olah `__ enhanced how the SKIP status works
+ when using templates with multiple iterations (`#4426`_).
+
+- `Marcin Gmurczyk `__ made it possible to
+ ignore order in values when comparing dictionaries (`#5007`_).
+
+- `Mohd Maaz Usmani `__ added support to control
+ the separator when appending to an existing value using `Set Suite Metadata`,
+ `Set Test Documentation` and other such keywords (`#5215`_).
+
+- `Luis Carlos `__ added explicit public API
+ to the `robot.api.parsing` module (`#5245`_).
+
+- `Theodore Georgomanolis `__ fixed `logging`
+ module usage so that the original log level is restored after execution (`#5262`_).
+
+Big thanks to Robot Framework Foundation, to community members listed above, and to
+everyone else who has tested preview releases, submitted bug reports, proposed
+enhancements, debugged problems, or otherwise helped with Robot Framework 7.2
+development.
+
+| `Pekka Klärck `_
+| Robot Framework lead developer
+
+Full list of fixes and enhancements
+===================================
+
+.. list-table::
+ :header-rows: 1
+
+ * - ID
+ - Type
+ - Priority
+ - Summary
+ - Added
+ * - `#3423`_
+ - enhancement
+ - critical
+ - Support JSON output files as part of execution
+ - beta 1
+ * - `#3676`_
+ - enhancement
+ - critical
+ - Libdoc localizations
+ - beta 1
+ * - `#4304`_
+ - enhancement
+ - critical
+ - New technology for Libdoc HTML outputs
+ - beta 1
+ * - `#5052`_
+ - bug
+ - high
+ - Invalid string representation for bytes outside ASCII range
+ - beta 1
+ * - `#5167`_
+ - bug
+ - high
+ - Crash if listener executes library keyword in `end_test` in the dry-run mode
+ - beta 1
+ * - `#5255`_
+ - bug
+ - high
+ - Logging APIs do not work if Robot Framework is run on thread
+ - beta 1
+ * - `#4959`_
+ - enhancement
+ - high
+ - Recognize library classes decorated with `@library` decorator regardless their name
+ - beta 1
+ * - `#5053`_
+ - enhancement
+ - high
+ - Support argument conversion with `Should Be Equal`
+ - beta 1
+ * - `#5160`_
+ - enhancement
+ - high
+ - Add execution errors and statistics to JSON output generated by Rebot
+ - beta 1
+ * - `#5257`_
+ - enhancement
+ - high
+ - `GROUP` syntax for grouping keywords and control structures
+ - beta 1
+ * - `#5260`_
+ - enhancement
+ - high
+ - Add log messages to result model that is build during execution and available to listeners
+ - beta 1
+ * - `#5170`_
+ - bug
+ - medium
+ - Failure in suite setup initiates exit-on-failure even if all tests have skip-on-failure active
+ - beta 1
+ * - `#5245`_
+ - bug
+ - medium
+ - `robot.api.parsing` doesn't have properly defined public API
+ - beta 1
+ * - `#5254`_
+ - bug
+ - medium
+ - Libdoc performance degradation starting from RF 6.0
+ - beta 1
+ * - `#5262`_
+ - bug
+ - medium
+ - `logging` module log level is not restored after execution
+ - beta 1
+ * - `#5266`_
+ - bug
+ - medium
+ - Messages logged by `start_test` and `end_test` listener methods are ignored
+ - beta 1
+ * - `#5268`_
+ - bug
+ - medium
+ - Listeners are not notified about actions they initiate
+ - beta 1
+ * - `#5269`_
+ - bug
+ - medium
+ - Recreating control structure results from JSON fails if they have messages mixed with iterations/branches
+ - beta 1
+ * - `#5274`_
+ - bug
+ - medium
+ - Problems with recommentation to use `$var` syntax if expression evaluation fails
+ - beta 1
+ * - `#5282`_
+ - bug
+ - medium
+ - `lineno` of keywords executed by `Run Keyword` variants is `None` in dry-run
+ - beta 1
+ * - `#5289`_
+ - bug
+ - medium
+ - Status of library keywords that are executed in dry-run is `NOT RUN`
+ - beta 1
+ * - `#4426`_
+ - enhancement
+ - medium
+ - All iterations of templated tests should be executed even if one is skipped
+ - beta 1
+ * - `#5007`_
+ - enhancement
+ - medium
+ - Collections: Support ignoring order in values when comparing dictionaries
+ - beta 1
+ * - `#5215`_
+ - enhancement
+ - medium
+ - Support controlling separator when appending current value using `Set Suite Metadata`, `Set Test Documentation` and other such keywords
+ - beta 1
+ * - `#5219`_
+ - enhancement
+ - medium
+ - Support stopping execution using `robot:exit-on-failure` tag
+ - beta 1
+ * - `#5223`_
+ - enhancement
+ - medium
+ - Allow setting variables with TEST scope in suite setup/teardown (not visible for tests or child suites)
+ - beta 1
+ * - `#5235`_
+ - enhancement
+ - medium
+ - Document that `Get Variable Value` and `Variable Should (Not) Exist` do not support named-argument syntax
+ - beta 1
+ * - `#5242`_
+ - enhancement
+ - medium
+ - Support inline flags for configuring custom embedded argument patterns
+ - beta 1
+ * - `#5251`_
+ - enhancement
+ - medium
+ - Allow listeners to remove log messages by setting them to `None`
+ - beta 1
+ * - `#5252`_
+ - enhancement
+ - medium
+ - Deprecate setting tags starting with a hyphen like `-tag` in `Test Tags`
+ - beta 1
+ * - `#5259`_
+ - enhancement
+ - medium
+ - Concatenating variables containing bytes should yield bytes
+ - beta 1
+ * - `#5264`_
+ - enhancement
+ - medium
+ - If test is skipped using `--skip` or `--skip-on-failure`, show used tags in test's message
+ - beta 1
+ * - `#5272`_
+ - enhancement
+ - medium
+ - Enhance recursion detection
+ - beta 1
+ * - `#5292`_
+ - enhancement
+ - medium
+ - `robot:skip` and `robot:exclude` tags do not support variables
+ - beta 1
+ * - `#5296`_
+ - enhancement
+ - medium
+ - Change source distribution format from deprecated `zip` to `tag.gz`
+ - rc 1
+ * - `#5202`_
+ - bug
+ - low
+ - Per-fle language configuration fails if there are two or more spaces after `Language:` prefix
+ - beta 1
+ * - `#5267`_
+ - bug
+ - low
+ - Message passed to `log_message` listener method has wrong type
+ - beta 1
+ * - `#5276`_
+ - bug
+ - low
+ - Templates should be explicitly prohibited with WHILE
+ - beta 1
+ * - `#5283`_
+ - bug
+ - low
+ - Documentation incorrectly claims that `--tagdoc` documentation supports HTML formatting
+ - beta 1
+ * - `#5288`_
+ - bug
+ - low
+ - `Message.id` broken if parent is not `Keyword` or `ExecutionErrors`
+ - beta 1
+ * - `#5295`_
+ - bug
+ - low
+ - Duplicate test name detection does not take variables into account
+ - beta 1
+ * - `#5155`_
+ - enhancement
+ - low
+ - Document where `log-.js` files created by `--splitlog` are saved
+ - beta 1
+ * - `#5216`_
+ - enhancement
+ - low
+ - Include `Message.html` in JSON results only if it is `True`
+ - beta 1
+ * - `#5238`_
+ - enhancement
+ - low
+ - Document return codes in `--help`
+ - beta 1
+ * - `#5286`_
+ - enhancement
+ - low
+ - Add suite and test `id` to JSON result model
+ - beta 1
+ * - `#5287`_
+ - enhancement
+ - low
+ - Add `type` attribute to `TestSuite` and `TestCase` objects
+ - beta 1
+
+Altogether 46 issues. View on the `issue tracker `__.
+
+.. _#3423: https://github.com/robotframework/robotframework/issues/3423
+.. _#3676: https://github.com/robotframework/robotframework/issues/3676
+.. _#4304: https://github.com/robotframework/robotframework/issues/4304
+.. _#5052: https://github.com/robotframework/robotframework/issues/5052
+.. _#5167: https://github.com/robotframework/robotframework/issues/5167
+.. _#5255: https://github.com/robotframework/robotframework/issues/5255
+.. _#4959: https://github.com/robotframework/robotframework/issues/4959
+.. _#5053: https://github.com/robotframework/robotframework/issues/5053
+.. _#5160: https://github.com/robotframework/robotframework/issues/5160
+.. _#5257: https://github.com/robotframework/robotframework/issues/5257
+.. _#5260: https://github.com/robotframework/robotframework/issues/5260
+.. _#5170: https://github.com/robotframework/robotframework/issues/5170
+.. _#5245: https://github.com/robotframework/robotframework/issues/5245
+.. _#5254: https://github.com/robotframework/robotframework/issues/5254
+.. _#5262: https://github.com/robotframework/robotframework/issues/5262
+.. _#5266: https://github.com/robotframework/robotframework/issues/5266
+.. _#5268: https://github.com/robotframework/robotframework/issues/5268
+.. _#5269: https://github.com/robotframework/robotframework/issues/5269
+.. _#5274: https://github.com/robotframework/robotframework/issues/5274
+.. _#5282: https://github.com/robotframework/robotframework/issues/5282
+.. _#5289: https://github.com/robotframework/robotframework/issues/5289
+.. _#4426: https://github.com/robotframework/robotframework/issues/4426
+.. _#5007: https://github.com/robotframework/robotframework/issues/5007
+.. _#5215: https://github.com/robotframework/robotframework/issues/5215
+.. _#5219: https://github.com/robotframework/robotframework/issues/5219
+.. _#5223: https://github.com/robotframework/robotframework/issues/5223
+.. _#5235: https://github.com/robotframework/robotframework/issues/5235
+.. _#5242: https://github.com/robotframework/robotframework/issues/5242
+.. _#5251: https://github.com/robotframework/robotframework/issues/5251
+.. _#5252: https://github.com/robotframework/robotframework/issues/5252
+.. _#5259: https://github.com/robotframework/robotframework/issues/5259
+.. _#5264: https://github.com/robotframework/robotframework/issues/5264
+.. _#5272: https://github.com/robotframework/robotframework/issues/5272
+.. _#5292: https://github.com/robotframework/robotframework/issues/5292
+.. _#5296: https://github.com/robotframework/robotframework/issues/5296
+.. _#5202: https://github.com/robotframework/robotframework/issues/5202
+.. _#5267: https://github.com/robotframework/robotframework/issues/5267
+.. _#5276: https://github.com/robotframework/robotframework/issues/5276
+.. _#5283: https://github.com/robotframework/robotframework/issues/5283
+.. _#5288: https://github.com/robotframework/robotframework/issues/5288
+.. _#5295: https://github.com/robotframework/robotframework/issues/5295
+.. _#5155: https://github.com/robotframework/robotframework/issues/5155
+.. _#5216: https://github.com/robotframework/robotframework/issues/5216
+.. _#5238: https://github.com/robotframework/robotframework/issues/5238
+.. _#5286: https://github.com/robotframework/robotframework/issues/5286
+.. _#5287: https://github.com/robotframework/robotframework/issues/5287
diff --git a/doc/releasenotes/rf-7.3.1.rst b/doc/releasenotes/rf-7.3.1.rst
new file mode 100644
index 00000000000..6387b0c7556
--- /dev/null
+++ b/doc/releasenotes/rf-7.3.1.rst
@@ -0,0 +1,139 @@
+=====================
+Robot Framework 7.3.1
+=====================
+
+.. default-role:: code
+
+`Robot Framework`_ 7.3.1 is the first bug fix release in the Robot Framework 7.3.x
+series. It fixes all reported regressions in `Robot Framework 7.3 `_.
+
+Questions and comments related to the release can be sent to the `#devel`
+channel on `Robot Framework Slack`_ and possible bugs submitted to
+the `issue tracker`_.
+
+If you have pip_ installed, just run
+
+::
+
+ pip install --upgrade robotframework
+
+to install the latest available stable release or use
+
+::
+
+ pip install robotframework==7.3.1
+
+to install exactly this version. Alternatively you can download the package
+from PyPI_ and install it manually. For more details and other installation
+approaches, see the `installation instructions`_.
+
+Robot Framework 7.3.1 was released on Monday June 16, 2025.
+
+.. _Robot Framework: http://robotframework.org
+.. _Robot Framework Foundation: http://robotframework.org/foundation
+.. _pip: http://pip-installer.org
+.. _PyPI: https://pypi.python.org/pypi/robotframework
+.. _issue tracker milestone: https://github.com/robotframework/robotframework/issues?q=milestone%3Av7.3.1
+.. _issue tracker: https://github.com/robotframework/robotframework/issues
+.. _robotframework-users: http://groups.google.com/group/robotframework-users
+.. _Slack: http://slack.robotframework.org
+.. _Robot Framework Slack: Slack_
+.. _installation instructions: ../../INSTALL.rst
+
+.. contents::
+ :depth: 2
+ :local:
+
+Most important enhancements
+===========================
+
+Parsing crashes if user keyword has invalid argument specification with type information
+----------------------------------------------------------------------------------------
+
+For example, this keyword caused parsing to crash so that the whole execution was
+prevented (`#5443`_):
+
+.. sourcecode:: robotframework
+
+ *** Keywords ***
+ Argument without default value after default values
+ [Arguments] ${a: int}=1 ${b: str}
+ No Operation
+
+This kind of invalid data is unlikely to be common, but the whole execution crashing
+is nevertheless severe. This bug also affected IDEs using Robot's parsing modules
+and caused annoying problems when the user had not finished writing the data.
+
+Keyword resolution change when using variable in setup/teardown keyword name
+----------------------------------------------------------------------------
+
+Earlier variables in setup/teardown keyword names were resolved before matching
+the name to available keywords. To support keywords accepting embedded arguments
+better, this was changed in Robot Framework 7.3 so that the initial name with
+variables was matched first (`#5367`__). That change made sense in general,
+but in the uncommon case that a keyword matched both a normal keyword and
+a keyword accepting embedded arguments, the latter now had a precedence.
+
+This behavioral change in Robot Framework 7.3 was not intended and the resulting
+behavior was also inconsistent with how precedence rules work normally. That part
+of the earlier change has now been reverted and nowadays keywords matching exactly
+after variables have been resolved again have priority over embedded matches
+(`#5444`_).
+
+__ https://github.com/robotframework/robotframework/issues/5367
+
+Acknowledgements
+================
+
+Robot Framework is developed with support from the Robot Framework Foundation
+and its 80+ member organizations. Join the journey — support the project by
+`joining the Foundation `_.
+
+In addition to the work sponsored by the foundation, this release got a contribution
+from `Pasi Saikkonen `_ who fixed the toggle icon in
+the log file when toggling a failed or skipped test (`#5322`_).
+
+Big thanks to the Foundation and to everyone who has submitted bug reports, debugged
+problems, or otherwise helped with Robot Framework development.
+
+| `Pekka Klärck `_
+| Robot Framework lead developer
+
+Full list of fixes and enhancements
+===================================
+
+.. list-table::
+ :header-rows: 1
+
+ * - ID
+ - Type
+ - Priority
+ - Summary
+ * - `#5443`_
+ - bug
+ - critical
+ - Parsing crashes if user keyword has invalid argument specification with type information
+ * - `#5444`_
+ - bug
+ - high
+ - Keyword matching exactly after replacing variables is not used with setup/teardown or with `Run Keyword` (regression)
+ * - `#5441`_
+ - enhancement
+ - high
+ - Update contribution guidelines
+ * - `#5322`_
+ - bug
+ - low
+ - Log: Toggle icon is stuck to `[+]` after toggling failed or skipped test
+ * - `#5447`_
+ - enhancement
+ - low
+ - Memory usage enhancement to `FileReader.readlines`
+
+Altogether 5 issues. View on the `issue tracker `__.
+
+.. _#5443: https://github.com/robotframework/robotframework/issues/5443
+.. _#5444: https://github.com/robotframework/robotframework/issues/5444
+.. _#5441: https://github.com/robotframework/robotframework/issues/5441
+.. _#5322: https://github.com/robotframework/robotframework/issues/5322
+.. _#5447: https://github.com/robotframework/robotframework/issues/5447
diff --git a/doc/releasenotes/rf-7.3.2.rst b/doc/releasenotes/rf-7.3.2.rst
new file mode 100644
index 00000000000..cfc90c0b079
--- /dev/null
+++ b/doc/releasenotes/rf-7.3.2.rst
@@ -0,0 +1,108 @@
+=====================
+Robot Framework 7.3.2
+=====================
+
+.. default-role:: code
+
+`Robot Framework`_ 7.3.2 is the second and the last planned bug fix release
+in the Robot Framework 7.3.x series. It fixes few regressions in earlier
+RF 7.3.x releases as well as some issues affecting also earlier releases.
+
+Questions and comments related to the release can be sent to the `#devel`
+channel on `Robot Framework Slack`_ and possible bugs submitted to
+the `issue tracker`_.
+
+If you have pip_ installed, just run
+
+::
+
+ pip install --upgrade robotframework
+
+to install the latest available release or use
+
+::
+
+ pip install robotframework==7.3.2
+
+to install exactly this version. Alternatively you can download the package
+from PyPI_ and install it manually. For more details and other installation
+approaches, see the `installation instructions`_.
+
+Robot Framework 7.3.2 was released on Friday July 4, 2025.
+
+.. _Robot Framework: http://robotframework.org
+.. _Robot Framework Foundation: http://robotframework.org/foundation
+.. _pip: http://pip-installer.org
+.. _PyPI: https://pypi.python.org/pypi/robotframework
+.. _issue tracker milestone: https://github.com/robotframework/robotframework/issues?q=milestone%3Av7.3.2
+.. _issue tracker: https://github.com/robotframework/robotframework/issues
+.. _robotframework-users: http://groups.google.com/group/robotframework-users
+.. _Slack: http://slack.robotframework.org
+.. _Robot Framework Slack: Slack_
+.. _installation instructions: ../../INSTALL.rst
+
+.. contents::
+ :depth: 2
+ :local:
+
+Acknowledgements
+================
+
+Robot Framework is developed with support from the Robot Framework Foundation
+and its 80+ member organizations. Join the journey — support the project by
+`joining the Foundation `_.
+
+Big thanks to the Foundation and to everyone who has submitted bug reports, debugged
+problems, or otherwise helped with Robot Framework development.
+
+| `Pekka Klärck `_
+| Robot Framework lead developer
+
+Full list of fixes
+==================
+
+.. list-table::
+ :header-rows: 1
+
+ * - ID
+ - Type
+ - Priority
+ - Summary
+ * - `#5455`_
+ - bug
+ - high
+ - Embedded arguments matching only after replacing variables do not work with `Run Keyword` or setup/teardown (regression in RF 7.3.1)
+ * - `#5456`_
+ - bug
+ - high
+ - French `Étant donné`, `Et` and `Mais` BDD prefixes don't work with keyword names starting with `que` or `qu'` (regression in RF 7.3)
+ * - `#5463`_
+ - bug
+ - high
+ - Messages and keywords by listener `end_test` method override original body when using JSON outputs if test has teardown
+ * - `#5464`_
+ - bug
+ - high
+ - `--flattenkeywords` doesn't work with JSON outputs
+ * - `#5466`_
+ - bug
+ - medium
+ - `--flattenkeywords` doesn't remove GROUP, VAR or RETURN
+ * - `#5467`_
+ - bug
+ - medium
+ - `ExecutionResult` ignores `include_keywords` argument with JSON outputs
+ * - `#5468`_
+ - bug
+ - medium
+ - Suite teardown failures are not handled properly with JSON outputs
+
+Altogether 7 issues. View on the `issue tracker `__.
+
+.. _#5455: https://github.com/robotframework/robotframework/issues/5455
+.. _#5464: https://github.com/robotframework/robotframework/issues/5464
+.. _#5463: https://github.com/robotframework/robotframework/issues/5463
+.. _#5456: https://github.com/robotframework/robotframework/issues/5456
+.. _#5466: https://github.com/robotframework/robotframework/issues/5466
+.. _#5467: https://github.com/robotframework/robotframework/issues/5467
+.. _#5468: https://github.com/robotframework/robotframework/issues/5468
diff --git a/doc/releasenotes/rf-7.3.rst b/doc/releasenotes/rf-7.3.rst
new file mode 100644
index 00000000000..8b54612e932
--- /dev/null
+++ b/doc/releasenotes/rf-7.3.rst
@@ -0,0 +1,637 @@
+===================
+Robot Framework 7.3
+===================
+
+.. default-role:: code
+
+`Robot Framework`_ 7.3 is a feature release with variable type conversion,
+enhancements and fixes related to timeouts, official Python 3.14 compatibility
+and various other exciting new features and high priority bug fixes.
+
+Questions and comments related to the release can be sent to the `#devel`
+channel on `Robot Framework Slack`_ and possible bugs submitted to
+the `issue tracker`_.
+
+If you have pip_ installed, just run
+
+::
+
+ pip install --upgrade robotframework
+
+to install the latest available stable release or use
+
+::
+
+ pip install robotframework==7.3
+
+to install exactly this version. Alternatively you can download the package
+from PyPI_ and install it manually. For more details and other installation
+approaches, see the `installation instructions`_.
+
+Robot Framework 7.3 was released on Friday May 30, 2025.
+
+.. _Robot Framework: http://robotframework.org
+.. _Robot Framework Foundation: http://robotframework.org/foundation
+.. _pip: http://pip-installer.org
+.. _PyPI: https://pypi.python.org/pypi/robotframework
+.. _issue tracker milestone: https://github.com/robotframework/robotframework/issues?q=milestone%3Av7.3
+.. _issue tracker: https://github.com/robotframework/robotframework/issues
+.. _robotframework-users: http://groups.google.com/group/robotframework-users
+.. _Slack: http://slack.robotframework.org
+.. _Robot Framework Slack: Slack_
+.. _installation instructions: ../../INSTALL.rst
+
+.. contents::
+ :depth: 2
+ :local:
+
+Most important enhancements
+===========================
+
+Variable type conversion
+------------------------
+
+The most important new feature in Robot Framework 7.3 is variable type conversion
+in the data (`#3278`_) and on the command line (`#2946`_). The syntax
+to specify variable types is `${name: type}` in the data and `name: type:value`
+on the command line, and the space after the colon is mandatory in both cases.
+Variable type conversion supports the same types that the `argument conversion`__
+supports. For example, `${number: int}` means that the value of the variable
+`${number}` is converted to an integer.
+
+__ http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#supported-conversions
+
+Variable conversion in data
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Variable types work in the Variables section, with the `VAR` syntax, when creating
+variables based on keyword return values, with FOR loops and, very importantly, with
+user keyword arguments. All these usages are demonstrated by the following examples:
+
+.. sourcecode:: robotframework
+
+ *** Variables ***
+ # Simple type.
+ ${VERSION: float} 7.3
+ # Parameterized type.
+ ${CRITICAL: list[int]} [3278, 5368, 5417]
+ # With @{list} variables the type specified the item type.
+ @{HIGH: int} 4173 5334 5386 5387
+ # With @{dict} variables the type specified the value type.
+ &{DATES: date} rc1=2025-05-08 final=2025-05-15
+ # Alternative syntax to specify both key and value types.
+ &{NUMBERS: int=float} 1=2.3 4=5.6
+
+ *** Test Cases ***
+ Variables section
+ # Validate above variables using the inline Python evaluation syntax.
+ # This syntax is much more complicated than the syntax used above!
+ Should Be Equal ${VERSION} ${{7.3}}
+ Should Be Equal ${CRITICAL} ${{[3278, 5368, 5417]}}
+ Should Be Equal ${HIGH} ${{[4173, 5334, 5386, 5387]}}
+ Should Be Equal ${DATES} ${{{'rc1': datetime.date(2025, 5, 8), 'final': datetime.date(2025, 5, 15)}}}
+ Should Be Equal ${NUMBERS} ${{{1: 2.3, 4: 5.6}}}
+
+ VAR syntax
+ # The VAR syntax supports types the same way as the Variables section
+ VAR ${number: int} 42
+ Should Be Equal ${number} ${42}
+
+ Assignment
+ # In simple cases the VAR syntax is more convenient.
+ ${number: int} = Set Variable 42
+ Should Be Equal ${number} ${42}
+ # In this example conversion is more useful.
+ ${match} ${version: float} = Should Match Regexp RF 7.3 ^RF (\\d+\\.\\d+)$
+ Should Be Equal ${match} RF 7.3
+ Should Be Equal ${version} ${7.3}
+
+ FOR loop
+ FOR ${fib: int} IN 0 1 1 2 3 5 8 13
+ Log ${fib}
+ END
+
+ Keyword arguments
+ # Argument conversion with user keywords is very convenient!
+ Move 10 down slow=no
+ # Conversion handles validation automatically. This usage fails.
+ Move 10 invalid
+
+ Embedded arguments
+ # Also embedded arguments can be converted.
+ Move 3.14 meters
+
+ *** Keywords ***
+ Move
+ [Arguments] ${distance: int} ${direction: Literal["UP", "DOWN"]} ${slow: bool}=True
+ Should Be Equal ${distance} ${10}
+ Should Be Equal ${direction} DOWN
+ Should Be Equal ${slow} ${False}
+
+ Move ${distance: int | float} meters
+ Should Be Equal ${distance} ${3.14}
+
+Variable conversion on command line
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Variable conversion works also with variables given from the command line using
+the `--variable` option. The syntax is `name: type:value` and, due to the space
+being mandatory, the whole option value typically needs to be quoted. Following
+examples demonstrate some possible usages for this functionality::
+
+ --variable "ITERATIONS: int:99"
+ --variable "PAYLOAD: dict:{'id': 1, 'name': 'Robot'}"
+ --variable "START_TIME: datetime:now"
+
+Notice that the last conversion uses the new `datetime` conversion that allows
+getting the current local date and time with the special value `now` (`#5440`_).
+
+Fixes and enhancements for timeouts
+-----------------------------------
+
+Several high priority and even critical issues related to timeouts have been fixed.
+Most of them are related to library keywords using `BuiltIn.run_keyword` which is
+a somewhat special case, but some problems occurred also with normal keywords.
+In addition to fixes, there have been some enhancements as well.
+
+Avoid output file corruption
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Library keywords can use `BuiltIn.run_keyword` as an API to execute other keywords.
+If Robot Framework timeouts occurred when that was done, the timeout could interrupt
+Robot Framework's own code that was preparing the new keyword to be executed.
+That situation was otherwise handled fine, but if the timeout occurred when Robot
+Framework was writing information to the output file, the output file could be
+corrupted and it was not possible to generate log and report after the execution.
+This severe problem has now been fixed by automatically pausing timeouts when
+`BuiltIn.run_keyword` is used (`#5417`_).
+
+Normally the odds that a timeout occurred after the parent keyword had called
+`BuiltIn.run_keyword`, but before the child keyword had actually started running,
+were pretty small, but if there were lof of such calls and also if child keywords
+logged lot of messages, the odds grew bigger. It is very likely, that some
+of the mysterious problems with output files being corrupted, that have been
+reported to our issue tracker, have been caused by this issue. Hopefully we get
+less such reports in the future!
+
+Other fixes related to `BuiltIn.run_keyword` and timeouts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are also some other fixes related to library keywords using `BuiltIn.run_keyword`
+when timeouts are enabled:
+
+- Timeouts are not deactivated after the child keyword returns (`#5422`_).
+ This problem occurred only outside Windows and actually prevented the above
+ bug corrupting output files outside Windows as well.
+- Order and position of logged messages is correct (`#5423`_).
+
+Other fixes related to timeouts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Logged messages respect the current log level (`#5395`_).
+- Writing messages to the debug file and to the console is not delayed (`#3644`_).
+
+Timeout related enhancements
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- It was discovered that libraries can easily handle Robot Framework's timeouts
+ so that they can do cleanup activities if needed. How to do that in practice
+ has been now documented in the User Guide (`#5377`_).
+- Timeout support with Dialogs (`#5386`_) and Process (`#5345`_, `#5376`_)
+ libraries has been enhanced. These enhancements are discussed separately below.
+
+Fix crash if library has implemented `__dir__` and `__getattr__`
+----------------------------------------------------------------
+
+Although implementing `__dir__` is pretty rare, hard crashes are always severe.
+As a concrete problem this bug prevented using the Faker tool directly as
+a library (`#5368`_).
+
+Enhancements to the Dialogs library
+-----------------------------------
+
+The Dialogs library is widely used in cases where something cannot be fully
+automated or execution needs to be paused for some reason. It got two major
+enhancements in this release.
+
+Support timeouts and close dialogs with Ctrl-C
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Robot Framework's timeouts are now finally able to kill opened dialogs (`#5386`_).
+Earlier if a timeout occurred when a dialog was open, the execution hang until
+the dialog was manually closed and the timeout stopped the execution then.
+The same fix also makes it possible to stop the execution with Ctrl-C even
+if a dialog is open.
+
+Enhanced look and feel
+~~~~~~~~~~~~~~~~~~~~~~
+
+The actual dialogs were enhanced in different ways (`#5334`_):
+
+- Dialogs got application and taskbar icons.
+- Font size has been increased a bit to make text easier to read.
+- More padding has been added around elements to make dialogs look better.
+ Buttons being separated from each others a bit more also avoids misclicks.
+- As the result of the above two changes, also the dialog size has increased.
+
+See `this comment`__ for an example how new and old dialogs look like.
+
+__ https://github.com/robotframework/robotframework/issues/5334#issuecomment-2761597900
+
+Enhancements to the Process library
+-----------------------------------
+
+Also the Process library got two major enhancements in this release.
+
+Avoid deadlock if process produces lot of output
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It has been possible to avoid the deadlock by redirecting `stdout` and `stderr`
+to files, but that is normally not necessary anymore (`#4173`_). Redirecting
+outputs to files is often a good idea anyway, and should be done at least if
+a process produces a huge amount of output.
+
+Better support for Robot Framework's timeouts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The Process library has its own timeout mechanism, but it now works better also
+with Robot Framework's test and keyword timeouts:
+
+- Robot Framework's timeouts were earlier not able to interrupt `Run Process` and
+ `Wait For Process` at all on Windows (`#5345`_). In the worst case the execution
+ could hang.
+- Nowadays the process that is waited for is killed if Robot Framework timeout
+ occurs (`#5376`_). This is better than leaving the process running on
+ the background.
+
+Python 3.14 compatibility
+-------------------------
+
+Robot Framework 7.3 is officially compatible with the forthcoming `Python 3.14`__
+release (`#5352`_). No code changes were needed so also older Robot Framework
+versions ought to work fine.
+
+__ https://docs.python.org/3.14/whatsnew/3.14.html
+
+Automatic code formatting
+-------------------------
+
+Robot Framework source code and also test code has been auto-formatted
+(`#5387`_). This is not really an enhancement in the tool itself, but
+automatic formatting makes it easier to create and review pull requests.
+
+Formatting is done using a combination of Ruff__, Black__ and isort__. These
+tools should not be used directly, but instead formatting should be done
+using an invoke__ task like::
+
+ invoke format
+
+__ https://docs.astral.sh/ruff/
+__ https://black.readthedocs.io/en/stable/
+__ https://pycqa.github.io/isort/
+__ https://www.pyinvoke.org/
+
+Backwards incompatible changes
+==============================
+
+All known backwards incompatible changes in this release are related to
+the variable conversion syntax, but `every change can break someones workflow`__
+so we recommend everyone to test this release before using it in production.
+
+__ https://xkcd.com/1172/
+
+Variable type syntax in data may clash with existing variables
+--------------------------------------------------------------
+
+The syntax to specify variable types in the data like `${x: int}` (`#3278`_)
+may clash with existing variables having names with colons. This is not very
+likely, though, because the type syntax requires having a space after the colon
+and names like `${x:int}` are thus not affected. If someone actually has
+a variable with a space after a colon, the space needs to be removed.
+
+Command line variable type syntax may clash with existing values
+----------------------------------------------------------------
+
+The variable type syntax can cause problems also with variables given from
+the command line (`#2946`_). Also the syntax to specify variables without a type
+uses a colon like `--variable NAME:value`, but because the type syntax requires
+a space after the colon like `--variable X: int:42`, there typically are no
+problems. In practice there are problems only if a value starts with a space and
+contains one or more colons::
+
+ --variable "NAME: this is :not: common"
+
+In such cases an explicit type needs to be added::
+
+ --variable "NAME: str: this is :not: common"
+
+Deprecated features
+===================
+
+Deprecated utility functions
+----------------------------
+
+The following functions and other utilities under the `robot.utils` package
+have been deprecated:
+
+- `is_string`, `is_bytes`, `is_number`, `is_integer` and `is_pathlike` have been
+ deprecated and should be replaced with `isinstance` like `isinstance(item, str)`
+ (`#5416`_).
+- `robot.utils.ET` has been deprecated and `xml.etree.ElementTree` should be
+ used instead (`#5415`_).
+
+Various other__ utilities__ have been deprecated in previous releases. Currently
+deprecation warnings related to all these utils are not visible by default,
+but they will be changed to more visible warnings in Robot Framework 8.0 and
+the plan is to remove the utils in Robot Framework 9.0. Use the PYTHONWARNINGS__
+environment variable or Python's `-W`__ option to make warnings more visible
+if you want to see is your tool using any deprecated APIs. For example,
+`-W error` turns all deprecation warnings to exceptions making them very
+easy to discover.
+
+__ https://github.com/robotframework/robotframework/issues/4150
+__ https://github.com/robotframework/robotframework/issues/4500
+__ https://docs.python.org/3/using/cmdline.html#envvar-PYTHONWARNINGS
+__ https://docs.python.org/3/using/cmdline.html#cmdoption-W
+
+Acknowledgements
+================
+
+Robot Framework is developed with support from the Robot Framework Foundation
+and its 80+ member organizations. Join the journey — support the project by
+`joining the Foundation `_.
+
+Robot Framework 7.3 team funded by the foundation consisted of `Pekka Klärck`_ and
+`Janne Härkönen `_. Janne worked only part-time and was
+mainly responsible on Libdoc related fixes. In addition to work done by them, the
+community has provided some great contributions:
+
+- `Tatu Aalto `__ worked with Pekka to implement
+ variable type conversion (`#3278`_), the biggest new feature in this release.
+ Huge thanks to Tatu and to his employer `OP `__, a member
+ of the `Robot Framework Foundation`_, for dedicating work time to make this
+ happen!
+
+- `@franzhaas `__ helped with the Process library.
+ He provided initial implementation both for avoiding deadlock (`#4173`_) and
+ for fixing Robot Framework timeout support on Windows (`#5345`_).
+
+- `Olivier Renault `__ fixed a bug with BDD prefixes
+ having same beginning (`#5340`_) and enhanced French BDD prefixes (`#5150`_).
+
+- `Gad Hassine `__ provided Arabic localization (`#5357`_).
+
+- `Lucian D. Crainic `__ added Italian Libdoc UI
+ translation (`#5351`_)
+
+Big thanks to Robot Framework Foundation, to community members listed above, and
+to everyone else who has tested preview releases, submitted bug reports, proposed
+enhancements, debugged problems, or otherwise helped with Robot Framework 7.3
+development.
+
+| `Pekka Klärck `_
+| Robot Framework lead developer
+
+Full list of fixes and enhancements
+===================================
+
+.. list-table::
+ :header-rows: 1
+
+ * - ID
+ - Type
+ - Priority
+ - Summary
+ * - `#5368`_
+ - bug
+ - critical
+ - Library with custom `__dir__` and attributes implemented via `__getattr__` causes crash
+ * - `#5417`_
+ - bug
+ - critical
+ - Output file can be corrupted if library keyword uses `BuiltIn.run_keyword` and timeout occurs
+ * - `#3278`_
+ - enhancement
+ - critical
+ - Variable type conversion
+ * - `#5352`_
+ - enhancement
+ - critical
+ - Python 3.14 compatibility
+ * - `#4173`_
+ - bug
+ - high
+ - Process: Avoid deadlock when standard streams are not redirected to files
+ * - `#5386`_
+ - bug
+ - high
+ - Dialogs: Not possible to stop execution with timeouts or by pressing Ctrl-C
+ * - `#2946`_
+ - enhancement
+ - high
+ - Variable type conversion with command line variables
+ * - `#5334`_
+ - enhancement
+ - high
+ - Dialogs: Enhance look and feel
+ * - `#5387`_
+ - enhancement
+ - high
+ - Automatic code formatting
+ * - `#3644`_
+ - bug
+ - medium
+ - Writing messages to debug file and to console is delayed when timeouts are used
+ * - `#4514`_
+ - bug
+ - medium
+ - Cannot interrupt `robot.run` or `robot.run_cli` and call it again
+ * - `#5098`_
+ - bug
+ - medium
+ - `buildout` cannot create start-up scripts using current entry point configuration
+ * - `#5330`_
+ - bug
+ - medium
+ - Keyword accepting embedded arguments cannot be used with variable containing characters used in keyword name
+ * - `#5340`_
+ - bug
+ - medium
+ - BDD prefixes with same beginning are not handled properly
+ * - `#5345`_
+ - bug
+ - medium
+ - Process: Test and keyword timeouts do not work when running processes on Windows
+ * - `#5358`_
+ - bug
+ - medium
+ - Libdoc: TypedDict documentation is broken in HTML output
+ * - `#5367`_
+ - bug
+ - medium
+ - Embedded arguments are not passed as objects when executed as setup/teardown
+ * - `#5393`_
+ - bug
+ - medium
+ - Cannot use keyword with parameterized special form like `TypeForm[param]` as type hint
+ * - `#5394`_
+ - bug
+ - medium
+ - Embedded arguments using custom regexps cannot be used with inline Python evaluation syntax
+ * - `#5395`_
+ - bug
+ - medium
+ - Messages logged when timeouts are active do not respect current log level
+ * - `#5399`_
+ - bug
+ - medium
+ - TEST scope variable set on suite level removes SUITE scope variable with same name
+ * - `#5405`_
+ - bug
+ - medium
+ - Extended variable assignment doesn't work with `@` or `&` syntax
+ * - `#5422`_
+ - bug
+ - medium
+ - Timeouts are deactivated if library keyword uses `BuiltIn.run_keyword` (except on Windows)
+ * - `#5423`_
+ - bug
+ - medium
+ - Log messages are in wrong order if library keyword uses `BuiltIn.run_keyword` and timeouts are used
+ * - `#5433`_
+ - bug
+ - medium
+ - Confusing error messages when adding incompatible objects to `TestSuite` structure
+ * - `#5150`_
+ - enhancement
+ - medium
+ - Enhance BDD support (GIVEN/WHEN/THEN) for French language
+ * - `#5351`_
+ - enhancement
+ - medium
+ - Add Italian Libdoc UI translation
+ * - `#5357`_
+ - enhancement
+ - medium
+ - Add Arabic localization
+ * - `#5376`_
+ - enhancement
+ - medium
+ - Process: Kill process if Robot's timeout occurs when waiting for process to end
+ * - `#5377`_
+ - enhancement
+ - medium
+ - Document how libraries can do cleanup activities if Robot's timeout occurs
+ * - `#5385`_
+ - enhancement
+ - medium
+ - Bundle logo to distribution package and make it available for external tools
+ * - `#5412`_
+ - enhancement
+ - medium
+ - Change keywords accepting configuration arguments as `**config` to use named-only arguments instead
+ * - `#5414`_
+ - enhancement
+ - medium
+ - Add explicit APIs to `robot` root package and to all sub packages
+ * - `#5416`_
+ - enhancement
+ - medium
+ - Deprecate `is_string`, `is_bytes`, `is_number`, `is_integer` and `is_pathlike` utility functions
+ * - `#5440`_
+ - enhancement
+ - medium
+ - Support `now` and `today` as special values in `datetime` and `date` conversion
+ * - `#5398`_
+ - bug
+ - low
+ - Variable assignment is not validated during parsing
+ * - `#5403`_
+ - bug
+ - low
+ - Confusing error message when using arguments with user keyword having invalid argument specification
+ * - `#5404`_
+ - bug
+ - low
+ - Time strings using same marker multiple times like `2 seconds 3 seconds` should be invalid
+ * - `#5418`_
+ - bug
+ - low
+ - DateTime: Getting timestamp as epoch seconds fails close to the epoch on Windows
+ * - `#5432`_
+ - bug
+ - low
+ - Small bugs in `robot.utils.Importer`
+ * - `#5083`_
+ - enhancement
+ - low
+ - Document that Process library removes trailing newline from stdout and stderr
+ * - `#5332`_
+ - enhancement
+ - low
+ - Make list of languages in Libdoc's default language selection dynamic
+ * - `#5396`_
+ - enhancement
+ - low
+ - Document limitations with embedded arguments utilizing custom regexps with variables
+ * - `#5397`_
+ - enhancement
+ - low
+ - Expose execution mode via `${OPTIONS.rpa}`
+ * - `#5415`_
+ - enhancement
+ - low
+ - Deprecate `robot.utils.ET` and use `xml.etree.ElementTree` instead
+ * - `#5424`_
+ - enhancement
+ - low
+ - Document ERROR level and that logging with it stops execution if `--exit-on-error` is enabled
+
+Altogether 46 issues. View on the `issue tracker `__.
+
+.. _#5368: https://github.com/robotframework/robotframework/issues/5368
+.. _#5417: https://github.com/robotframework/robotframework/issues/5417
+.. _#3278: https://github.com/robotframework/robotframework/issues/3278
+.. _#5352: https://github.com/robotframework/robotframework/issues/5352
+.. _#4173: https://github.com/robotframework/robotframework/issues/4173
+.. _#5386: https://github.com/robotframework/robotframework/issues/5386
+.. _#2946: https://github.com/robotframework/robotframework/issues/2946
+.. _#5334: https://github.com/robotframework/robotframework/issues/5334
+.. _#5387: https://github.com/robotframework/robotframework/issues/5387
+.. _#3644: https://github.com/robotframework/robotframework/issues/3644
+.. _#4514: https://github.com/robotframework/robotframework/issues/4514
+.. _#5098: https://github.com/robotframework/robotframework/issues/5098
+.. _#5330: https://github.com/robotframework/robotframework/issues/5330
+.. _#5340: https://github.com/robotframework/robotframework/issues/5340
+.. _#5345: https://github.com/robotframework/robotframework/issues/5345
+.. _#5358: https://github.com/robotframework/robotframework/issues/5358
+.. _#5367: https://github.com/robotframework/robotframework/issues/5367
+.. _#5393: https://github.com/robotframework/robotframework/issues/5393
+.. _#5394: https://github.com/robotframework/robotframework/issues/5394
+.. _#5395: https://github.com/robotframework/robotframework/issues/5395
+.. _#5399: https://github.com/robotframework/robotframework/issues/5399
+.. _#5405: https://github.com/robotframework/robotframework/issues/5405
+.. _#5422: https://github.com/robotframework/robotframework/issues/5422
+.. _#5423: https://github.com/robotframework/robotframework/issues/5423
+.. _#5433: https://github.com/robotframework/robotframework/issues/5433
+.. _#5150: https://github.com/robotframework/robotframework/issues/5150
+.. _#5351: https://github.com/robotframework/robotframework/issues/5351
+.. _#5357: https://github.com/robotframework/robotframework/issues/5357
+.. _#5376: https://github.com/robotframework/robotframework/issues/5376
+.. _#5377: https://github.com/robotframework/robotframework/issues/5377
+.. _#5385: https://github.com/robotframework/robotframework/issues/5385
+.. _#5412: https://github.com/robotframework/robotframework/issues/5412
+.. _#5414: https://github.com/robotframework/robotframework/issues/5414
+.. _#5416: https://github.com/robotframework/robotframework/issues/5416
+.. _#5440: https://github.com/robotframework/robotframework/issues/5440
+.. _#5398: https://github.com/robotframework/robotframework/issues/5398
+.. _#5403: https://github.com/robotframework/robotframework/issues/5403
+.. _#5404: https://github.com/robotframework/robotframework/issues/5404
+.. _#5418: https://github.com/robotframework/robotframework/issues/5418
+.. _#5432: https://github.com/robotframework/robotframework/issues/5432
+.. _#5083: https://github.com/robotframework/robotframework/issues/5083
+.. _#5332: https://github.com/robotframework/robotframework/issues/5332
+.. _#5396: https://github.com/robotframework/robotframework/issues/5396
+.. _#5397: https://github.com/robotframework/robotframework/issues/5397
+.. _#5415: https://github.com/robotframework/robotframework/issues/5415
+.. _#5424: https://github.com/robotframework/robotframework/issues/5424
diff --git a/doc/releasenotes/rf-7.3rc1.rst b/doc/releasenotes/rf-7.3rc1.rst
new file mode 100644
index 00000000000..9b163409a64
--- /dev/null
+++ b/doc/releasenotes/rf-7.3rc1.rst
@@ -0,0 +1,593 @@
+=======================================
+Robot Framework 7.3 release candidate 1
+=======================================
+
+.. default-role:: code
+
+`Robot Framework`_ 7.3 is a feature release with variable type conversion,
+enhancements and fixes related to timeouts, and various other exciting new
+features and high priority bug fixes. This release candidate contains all
+planned code changes.
+
+Questions and comments related to the release can be sent to the `#devel`
+channel on `Robot Framework Slack`_ and possible bugs submitted to
+the `issue tracker`_.
+
+If you have pip_ installed, just run
+
+::
+
+ pip install --pre --upgrade robotframework
+
+to install the latest available release or use
+
+::
+
+ pip install robotframework==7.3rc1
+
+to install exactly this version. Alternatively you can download the package
+from PyPI_ and install it manually. For more details and other installation
+approaches, see the `installation instructions`_.
+
+Robot Framework 7.3 rc 1 was released on Thursday May 8, 2025.
+It was followed by the `second release candidate `_
+on Monday May 19, 2025.
+
+.. _Robot Framework: http://robotframework.org
+.. _Robot Framework Foundation: http://robotframework.org/foundation
+.. _pip: http://pip-installer.org
+.. _PyPI: https://pypi.python.org/pypi/robotframework
+.. _issue tracker milestone: https://github.com/robotframework/robotframework/issues?q=milestone%3Av7.3
+.. _issue tracker: https://github.com/robotframework/robotframework/issues
+.. _robotframework-users: http://groups.google.com/group/robotframework-users
+.. _Slack: http://slack.robotframework.org
+.. _Robot Framework Slack: Slack_
+.. _installation instructions: ../../INSTALL.rst
+
+.. contents::
+ :depth: 2
+ :local:
+
+Most important enhancements
+===========================
+
+Variable type conversion
+------------------------
+
+The most important new feature in Robot Framework 7.3 is variable type conversion
+(`#3278`_). The syntax to specify variable types is `${name: type}` and the space
+after the colon is mandatory. Variable type conversion supports the same types
+that the `argument conversion`__ supports. For example, `${number: int}`
+means that the value of the variable `${number}` is converted to an integer.
+
+__ http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#supported-conversions
+
+Variable types work in the Variables section, with the `VAR` syntax, when creating
+variables based on keyword return values and, very importantly, with user keyword
+arguments. All these usages are demonstrated by the following examples:
+
+.. sourcecode:: robotframework
+
+ *** Variables ***
+ # Simple type.
+ ${VERSION: float} 7.3
+ # Parameterized type.
+ ${CRITICAL: list[int]} [3278, 5368, 5417]
+ # With @{list} variables the type specified the item type.
+ @{HIGH: int} 4173 5334 5386 5387
+ # With @{dict} variables the type specified the value type.
+ &{DATES: date} rc1=2025-05-08 final=2025-05-15
+ # Alternative syntax to specify both key and value types.
+ &{NUMBERS: int=float} 1=2.3 4=5.6
+
+ *** Test Cases ***
+ Variables section
+ # Validate above variables using the inline Python evaluation syntax.
+ # This syntax is much more complicated than the syntax used above!
+ Should Be Equal ${VERSION} ${{7.3}}
+ Should Be Equal ${CRITICAL} ${{[3278, 5368, 5417]}}
+ Should Be Equal ${HIGH} ${{[4173, 5334, 5386, 5387]}}
+ Should Be Equal ${DATES} ${{{'rc1': datetime.date(2025, 5, 8), 'final': datetime.date(2025, 5, 15)}}}
+ Should Be Equal ${NUMBERS} ${{{1: 2.3, 4: 5.6}}}
+
+ VAR syntax
+ # The VAR syntax supports types the same way as the Variables section
+ VAR ${number: int} 42
+ Should Be Equal ${number} ${42}
+
+ Assignment
+ # In simple cases the VAR syntax is more convenient.
+ ${number: int} = Set Variable 42
+ Should Be Equal ${number} ${42}
+ # In this example conversion is more useful.
+ ${match} ${version: float} = Should Match Regexp RF 7.3 ^RF (\\d+\\.\\d+)$
+ Should Be Equal ${match} RF 7.3
+ Should Be Equal ${version} ${7.3}
+
+ Keyword arguments
+ # Argument conversion with user keywords is very convenient!
+ Move 10 down slow=no
+ # Conversion handles validation automatically. This usage fails.
+ Move 10 invalid
+
+ Embedded arguments
+ # Also embedded arguments can be converted.
+ Move 3.14 meters
+
+ *** Keywords ***
+ Move
+ [Arguments] ${distance: int} ${direction: Literal["UP", "DOWN"]} ${slow: bool}=True
+ Should Be Equal ${distance} ${10}
+ Should Be Equal ${direction} DOWN
+ Should Be Equal ${slow} ${False}
+
+ Move ${distance: int | float} meters
+ Should Be Equal ${distance} ${3.14}
+
+Fixes and enhancements for timeouts
+-----------------------------------
+
+Several high priority and even critical issues related to timeouts have been fixed.
+Most of them are related to library keywords using `BuiltIn.run_keyword` which is
+a somewhat special case, but some problems occurred also with normal keywords.
+In addition to fixes, there have been some enhancements as well.
+
+Avoid output file corruption
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Library keywords can use `BuiltIn.run_keyword` as an API to execute other keywords.
+If Robot Framework timeouts occur when that is done, the timeout can interrupt
+Robot Framework's own code that is preparing the new keyword to be executed.
+That situation is otherwise handled fine, but if the timeout occurs when Robot
+Framework is writing information to the output file, the output file can be
+corrupted and it is not possible to generate log and report after the execution.
+This severe problem has now been fixed by automatically pausing timeouts when
+`BuiltIn.run_keyword` is used (`#5417`_).
+
+Normally the odds that a timeout occurs after the parent keyword has called
+`BuiltIn.run_keyword` but before the child keyword has actually started running
+are pretty small, but if there are lof of such calls and also if child keywords
+write a lot of log messages, the odds grow bigger. It is very likely that some
+of the mysterious problems with output files being corrupted that have been
+reported to our issue tracker have been caused by this issue. Hopefully we get
+less such reports in the future!
+
+Other fixes related to `BuiltIn.run_keyword` and timeouts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are also some other fixes related to library keywords using `BuiltIn.run_keyword`
+when timeouts are enabled:
+
+- Timeouts are not deactivated after the child keyword returns (`#5422`_).
+ This problem occurred only outside Windows and actually prevented the above
+ bug corrupting output files outside Windows as well.
+- Order and position of logged messages is correct (`#5423`_).
+
+Other fixes related to timeouts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Logged messages respect the current log level (`#5395`_).
+- Writing messages to the debug file and to the console is not delayed (`#3644`_).
+
+Timeout related enhancements
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- It was discovered that libraries can easily handle Robot Framework's timeouts
+ so that they can do cleanup activities if needed. How to do that in practice
+ has been now documented in the User Guide (`#5377`_).
+- Timeout support with Dialogs (`#5386`_) and Process (`#5345`_, `#5376`_)
+ libraries has been enhanced. These enhancements are discussed separately below.
+
+Fix crash if library has implemented `__dir__` and `__getattr__`
+----------------------------------------------------------------
+
+Although implementing `__dir__` is pretty rare, hard crashes are always severe.
+As a concrete problem this bug prevented using the Faker tool directly as
+a library (`#5368`_).
+
+Enhancements to the Dialogs library
+-----------------------------------
+
+The Dialogs library is widely used in cases where something cannot be fully
+automated or execution needs to be paused for some reason. It got two major
+enhancements in this release.
+
+Support timeouts and closing with Ctrl-C
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Robot Framework's timeouts are now finally able to kill opened dialogs (`#5386`_).
+Earlier execution hang indefinitely if dialogs were open even if a timeout occurred,
+and the timeout was really activated only after the dialog was manually closed.
+The same fix also makes it possible to stop the execution with Ctrl-C even if
+a dialog would be open.
+
+Enhanced look and feel
+~~~~~~~~~~~~~~~~~~~~~~
+
+The actual dialogs were enhanced in different ways (`#5334`_):
+
+- Dialogs got application and taskbar icons.
+- Font size has been increased a bit to make text easier to read.
+- More padding has been added around elements to make dialogs look better.
+ Buttons being separated from each others a bit more also avoids misclicks.
+- As the result of the above two changes, also the dialog size has increased.
+
+See `this comment`__ for an example how new and old dialogs look like.
+
+__ https://github.com/robotframework/robotframework/issues/5334#issuecomment-2761597900
+
+Enhancements to the Process library
+-----------------------------------
+
+Also the Process library got two major enhancements in this release.
+
+Avoid deadlock if process produces lot of output
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It has been possible to avoid the deadlock by redirecting `stdout` and `stderr`
+to files, but that is not necessary anymore (`#4173`_). Redirecting outputs to
+files is often a good idea anyway, and should be done at least if a process
+produces a huge amount of output.
+
+Better support for Robot Framework's timeouts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The Process library has its own timeout mechanism, but it now works better also
+with Robot Framework's test and keyword timeouts:
+
+- Robot Framework's timeouts were not able to interrupt `Run Process` and
+ `Wait For Process` at all on Windows earlier (`#5345`_). In the worst case
+ the execution could hang.
+- Nowadays the process that is waited for is killed if Robot Framework timeout
+ occurs (`#5376`_). This is better than leaving the process running on
+ the background.
+
+Automatic code formatting
+-------------------------
+
+Robot Framework source code and also test code has been auto-formatted
+(`#5387`_). This is not really an enhancement in the tool itself, but
+automatic formatting makes it easier to create and review pull requests.
+
+Formatting is done using a combination of Ruff__, Black__ and isort__. These
+tools should not be used directly, but instead formatting should be done
+using an invoke__ task like::
+
+ invoke format
+
+More detailed instructions will be written to the `contribution guidelines`__
+in the near future.
+
+__ https://docs.astral.sh/ruff/
+__ https://black.readthedocs.io/en/stable/
+__ https://pycqa.github.io/isort/
+__ https://www.pyinvoke.org/
+__ https://github.com/robotframework/robotframework/blob/master/CONTRIBUTING.rst
+
+Backwards incompatible changes
+==============================
+
+There is only one known backwards incompatible change in this release, but
+`every change can break someones workflow`__.
+
+__ https://xkcd.com/1172/
+
+Variable type syntax may clash with existing variables
+------------------------------------------------------
+
+The syntax to specify variable types like `${x: int}` (`#3278`_) may clash with
+existing variables having names with colons. This is not very likely, though,
+because the type syntax requires having a space after the colon and names like
+`${foo:bar}` are thus not affected. If someone actually has a variable with
+a space after a colon, the space needs to be removed.
+
+Deprecated features
+===================
+
+Deprecated utility functions
+----------------------------
+
+The following functions and other utilities under the `robot.utils` package
+have been deprecated:
+
+- `is_string`, `is_bytes`, `is_number`, `is_integer` and `is_pathlike` have been
+ deprecated and should be replaced with `isinstance` like `isinstance(item, str)`
+ and `isinstance(item, int)` (`#5416`_).
+- `robot.utils.ET` has been deprecated and `xml.etree.ElementTree` should be
+ used instead (`#5415`_).
+
+Various other__ utilities__ have been deprecated in previous releases. Currently
+deprecation warnings related to all these utils are not visible by default,
+but they will be changed to more visible warnings in Robot Framework 8.0 and
+the plan is to remove the utils in Robot Framework 9.0. Use the PYTHONWARNINGS__
+environment variable or Python's `-W`__ option to make warnings more visible
+if you want to see is your tool using any deprecated APIs. For example,
+`-W error` turns all deprecation warnings to exceptions making them very
+easy to discover.
+
+__ https://github.com/robotframework/robotframework/issues/4150
+__ https://github.com/robotframework/robotframework/issues/4500
+__ https://docs.python.org/3/using/cmdline.html#envvar-PYTHONWARNINGS
+__ https://docs.python.org/3/using/cmdline.html#cmdoption-W
+
+Acknowledgements
+================
+
+Robot Framework development is sponsored by the `Robot Framework Foundation`_
+and its over 70 member organizations. If your organization is using Robot Framework
+and benefiting from it, consider joining the foundation to support its
+development as well.
+
+Robot Framework 7.3 team funded by the foundation consisted of `Pekka Klärck`_ and
+`Janne Härkönen `_. Janne worked only part-time and was
+mainly responsible on Libdoc related fixes. In addition to work done by them, the
+community has provided some great contributions:
+
+- `Tatu Aalto `__ worked with Pekka to implement
+ variable type conversion (`#3278`_). That was big task so huge thanks for
+ Tatu and his employer `OP `__ who let Tatu to use his
+ work time for this enhancement.
+
+- `@franzhaas `__ helped with the Process library.
+ He provided initial implementation both for avoiding deadlock (`#4173`_) and
+ for fixing Robot Framework timeout support on Windows (`#5345`_).
+
+- `Olivier Renault `__ fixed a bug with BDD prefixes
+ having same beginning (`#5340`_) and enhanced French BDD prefixes (`#5150`_).
+
+- `Gad Hassine `__ provided Arabic localization (`#5357`_).
+
+- `Lucian D. Crainic `__ added Italian Libdoc UI
+ translation (`#5351`_)
+
+Big thanks to Robot Framework Foundation, to community members listed above, and to
+everyone else who has tested preview releases, submitted bug reports, proposed
+enhancements, debugged problems, or otherwise helped with Robot Framework 7.3
+development.
+
+| `Pekka Klärck `_
+| Robot Framework lead developer
+
+Full list of fixes and enhancements
+===================================
+
+
+.. list-table::
+ :header-rows: 1
+
+ * - ID
+ - Type
+ - Priority
+ - Summary
+ - Added
+ * - `#5368`_
+ - bug
+ - critical
+ - Library with custom `__dir__` and attributes implemented via `__getattr__` causes crash
+ - rc 1
+ * - `#5417`_
+ - bug
+ - critical
+ - Output file can be corrupted if library keyword uses `BuiltIn.run_keyword` and timeout occurs
+ - rc 1
+ * - `#3278`_
+ - enhancement
+ - critical
+ - Variable type conversion
+ - rc 1
+ * - `#4173`_
+ - bug
+ - high
+ - Process: Avoid deadlock when standard streams are not redirected to files
+ - rc 1
+ * - `#5386`_
+ - bug
+ - high
+ - Dialogs: Not possible to stop execution with timeouts or by pressing Ctrl-C
+ - rc 1
+ * - `#5334`_
+ - enhancement
+ - high
+ - Dialogs: Enhance look and feel
+ - rc 1
+ * - `#5387`_
+ - enhancement
+ - high
+ - Automatic code formatting
+ - rc 1
+ * - `#3644`_
+ - bug
+ - medium
+ - Writing messages to debug file and to console is delayed when timeouts are used
+ - rc 1
+ * - `#5330`_
+ - bug
+ - medium
+ - Keyword accepting embedded arguments cannot be used with variable containing characters used in keyword name
+ - rc 1
+ * - `#5340`_
+ - bug
+ - medium
+ - BDD prefixes with same beginning are not handled properly
+ - rc 1
+ * - `#5345`_
+ - bug
+ - medium
+ - Process: Test and keyword timeouts do not work when running processes on Windows
+ - rc 1
+ * - `#5358`_
+ - bug
+ - medium
+ - Libdoc: TypedDict documentation is broken in HTML output
+ - rc 1
+ * - `#5367`_
+ - bug
+ - medium
+ - Embedded arguments are not passed as objects when executed as setup/teardown
+ - rc 1
+ * - `#5393`_
+ - bug
+ - medium
+ - Cannot use keyword with parameterized special form like `TypeForm[param]` as type hint
+ - rc 1
+ * - `#5394`_
+ - bug
+ - medium
+ - Embedded arguments using custom regexps cannot be used with inline Python evaluation syntax
+ - rc 1
+ * - `#5395`_
+ - bug
+ - medium
+ - Messages logged when timeouts are active do not respect current log level
+ - rc 1
+ * - `#5399`_
+ - bug
+ - medium
+ - TEST scope variable set on suite level removes SUITE scope variable with same name
+ - rc 1
+ * - `#5405`_
+ - bug
+ - medium
+ - Extended variable assignment doesn't work with `@` or `&` syntax
+ - rc 1
+ * - `#5422`_
+ - bug
+ - medium
+ - Timeouts are deactivated if library keyword uses `BuiltIn.run_keyword` (except on Windows)
+ - rc 1
+ * - `#5423`_
+ - bug
+ - medium
+ - Log messages are in wrong order if library keyword uses `BuiltIn.run_keyword` and timeouts are used
+ - rc 1
+ * - `#5150`_
+ - enhancement
+ - medium
+ - Enhance BDD support (GIVEN/WHEN/THEN) for French language
+ - rc 1
+ * - `#5351`_
+ - enhancement
+ - medium
+ - Add Italian Libdoc UI translation
+ - rc 1
+ * - `#5357`_
+ - enhancement
+ - medium
+ - Add Arabic localization
+ - rc 1
+ * - `#5376`_
+ - enhancement
+ - medium
+ - Process: Kill process if Robot's timeout occurs when waiting for process to end
+ - rc 1
+ * - `#5377`_
+ - enhancement
+ - medium
+ - Document how libraries can do cleanup activities if Robot's timeout occurs
+ - rc 1
+ * - `#5385`_
+ - enhancement
+ - medium
+ - Bundle logo to distribution package and make it available for external tools
+ - rc 1
+ * - `#5412`_
+ - enhancement
+ - medium
+ - Change keywords accepting configuration arguments as `**config` to use named-only arguments instead
+ - rc 1
+ * - `#5414`_
+ - enhancement
+ - medium
+ - Add explicit APIs to `robot` root package and to all sub packages
+ - rc 1
+ * - `#5416`_
+ - enhancement
+ - medium
+ - Deprecate `is_string`, `is_bytes`, `is_number`, `is_integer` and `is_pathlike` utility functions
+ - rc 1
+ * - `#5398`_
+ - bug
+ - low
+ - Variable assignment is not validated during parsing
+ - rc 1
+ * - `#5403`_
+ - bug
+ - low
+ - Confusing error message when using arguments with user keyword having invalid argument specification
+ - rc 1
+ * - `#5404`_
+ - bug
+ - low
+ - Time strings using same marker multiple times like `2 seconds 3 seconds` should be invalid
+ - rc 1
+ * - `#5418`_
+ - bug
+ - low
+ - DateTime: Getting timestamp as epoch seconds fails close to the epoch on Windows
+ - rc 1
+ * - `#5332`_
+ - enhancement
+ - low
+ - Make list of languages in Libdoc's default language selection dynamic
+ - rc 1
+ * - `#5396`_
+ - enhancement
+ - low
+ - Document limitations with embedded arguments utilizing custom regexps with variables
+ - rc 1
+ * - `#5397`_
+ - enhancement
+ - low
+ - Expose execution mode via `${OPTIONS.rpa}`
+ - rc 1
+ * - `#5415`_
+ - enhancement
+ - low
+ - Deprecate `robot.utils.ET` and use `xml.etree.ElementTree` instead
+ - rc 1
+ * - `#5424`_
+ - enhancement
+ - low
+ - Document ERROR level and that logging with it stops execution if `--exit-on-error` is enabled
+ - rc 1
+
+Altogether 38 issues. View on the `issue tracker `__.
+
+.. _#5368: https://github.com/robotframework/robotframework/issues/5368
+.. _#5417: https://github.com/robotframework/robotframework/issues/5417
+.. _#3278: https://github.com/robotframework/robotframework/issues/3278
+.. _#4173: https://github.com/robotframework/robotframework/issues/4173
+.. _#5386: https://github.com/robotframework/robotframework/issues/5386
+.. _#5334: https://github.com/robotframework/robotframework/issues/5334
+.. _#5387: https://github.com/robotframework/robotframework/issues/5387
+.. _#3644: https://github.com/robotframework/robotframework/issues/3644
+.. _#5330: https://github.com/robotframework/robotframework/issues/5330
+.. _#5340: https://github.com/robotframework/robotframework/issues/5340
+.. _#5345: https://github.com/robotframework/robotframework/issues/5345
+.. _#5358: https://github.com/robotframework/robotframework/issues/5358
+.. _#5367: https://github.com/robotframework/robotframework/issues/5367
+.. _#5393: https://github.com/robotframework/robotframework/issues/5393
+.. _#5394: https://github.com/robotframework/robotframework/issues/5394
+.. _#5395: https://github.com/robotframework/robotframework/issues/5395
+.. _#5399: https://github.com/robotframework/robotframework/issues/5399
+.. _#5405: https://github.com/robotframework/robotframework/issues/5405
+.. _#5422: https://github.com/robotframework/robotframework/issues/5422
+.. _#5423: https://github.com/robotframework/robotframework/issues/5423
+.. _#5150: https://github.com/robotframework/robotframework/issues/5150
+.. _#5351: https://github.com/robotframework/robotframework/issues/5351
+.. _#5357: https://github.com/robotframework/robotframework/issues/5357
+.. _#5376: https://github.com/robotframework/robotframework/issues/5376
+.. _#5377: https://github.com/robotframework/robotframework/issues/5377
+.. _#5385: https://github.com/robotframework/robotframework/issues/5385
+.. _#5412: https://github.com/robotframework/robotframework/issues/5412
+.. _#5414: https://github.com/robotframework/robotframework/issues/5414
+.. _#5416: https://github.com/robotframework/robotframework/issues/5416
+.. _#5398: https://github.com/robotframework/robotframework/issues/5398
+.. _#5403: https://github.com/robotframework/robotframework/issues/5403
+.. _#5404: https://github.com/robotframework/robotframework/issues/5404
+.. _#5418: https://github.com/robotframework/robotframework/issues/5418
+.. _#5332: https://github.com/robotframework/robotframework/issues/5332
+.. _#5396: https://github.com/robotframework/robotframework/issues/5396
+.. _#5397: https://github.com/robotframework/robotframework/issues/5397
+.. _#5415: https://github.com/robotframework/robotframework/issues/5415
+.. _#5424: https://github.com/robotframework/robotframework/issues/5424
diff --git a/doc/releasenotes/rf-7.3rc2.rst b/doc/releasenotes/rf-7.3rc2.rst
new file mode 100644
index 00000000000..f257ba49bb6
--- /dev/null
+++ b/doc/releasenotes/rf-7.3rc2.rst
@@ -0,0 +1,625 @@
+=======================================
+Robot Framework 7.3 release candidate 2
+=======================================
+
+.. default-role:: code
+
+`Robot Framework`_ 7.3 is a feature release with variable type conversion,
+enhancements and fixes related to timeouts, official Python 3.14 compatibility
+and various other exciting new features and high priority bug fixes. This
+release candidate contains all planned code changes.
+
+Questions and comments related to the release can be sent to the `#devel`
+channel on `Robot Framework Slack`_ and possible bugs submitted to
+the `issue tracker`_.
+
+If you have pip_ installed, just run
+
+::
+
+ pip install --pre --upgrade robotframework
+
+to install the latest available release or use
+
+::
+
+ pip install robotframework==7.3rc2
+
+to install exactly this version. Alternatively you can download the package
+from PyPI_ and install it manually. For more details and other installation
+approaches, see the `installation instructions`_.
+
+Robot Framework 7.3 rc 2 was released on Monday May 19, 2025. Compared to the
+`first release candidate `_, it mainly contains some more
+enhancements related to variable type conversion and further fixes related to
+timeouts. It was followed by the third release candidate on Wednesday May 21, 2025.
+
+.. _Robot Framework: http://robotframework.org
+.. _Robot Framework Foundation: http://robotframework.org/foundation
+.. _pip: http://pip-installer.org
+.. _PyPI: https://pypi.python.org/pypi/robotframework
+.. _issue tracker milestone: https://github.com/robotframework/robotframework/issues?q=milestone%3Av7.3
+.. _issue tracker: https://github.com/robotframework/robotframework/issues
+.. _robotframework-users: http://groups.google.com/group/robotframework-users
+.. _Slack: http://slack.robotframework.org
+.. _Robot Framework Slack: Slack_
+.. _installation instructions: ../../INSTALL.rst
+
+.. contents::
+ :depth: 2
+ :local:
+
+Most important enhancements
+===========================
+
+Variable type conversion
+------------------------
+
+The most important new feature in Robot Framework 7.3 is variable type conversion
+(`#3278`_). The syntax to specify variable types is `${name: type}` and the space
+after the colon is mandatory. Variable type conversion supports the same types
+that the `argument conversion`__ supports. For example, `${number: int}`
+means that the value of the variable `${number}` is converted to an integer.
+
+__ http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#supported-conversions
+
+Variable types work in the Variables section, with the `VAR` syntax, when creating
+variables based on keyword return values, with FOR loops and, very importantly, with
+user keyword arguments. All these usages are demonstrated by the following examples:
+
+.. sourcecode:: robotframework
+
+ *** Variables ***
+ # Simple type.
+ ${VERSION: float} 7.3
+ # Parameterized type.
+ ${CRITICAL: list[int]} [3278, 5368, 5417]
+ # With @{list} variables the type specified the item type.
+ @{HIGH: int} 4173 5334 5386 5387
+ # With @{dict} variables the type specified the value type.
+ &{DATES: date} rc1=2025-05-08 final=2025-05-15
+ # Alternative syntax to specify both key and value types.
+ &{NUMBERS: int=float} 1=2.3 4=5.6
+
+ *** Test Cases ***
+ Variables section
+ # Validate above variables using the inline Python evaluation syntax.
+ # This syntax is much more complicated than the syntax used above!
+ Should Be Equal ${VERSION} ${{7.3}}
+ Should Be Equal ${CRITICAL} ${{[3278, 5368, 5417]}}
+ Should Be Equal ${HIGH} ${{[4173, 5334, 5386, 5387]}}
+ Should Be Equal ${DATES} ${{{'rc1': datetime.date(2025, 5, 8), 'final': datetime.date(2025, 5, 15)}}}
+ Should Be Equal ${NUMBERS} ${{{1: 2.3, 4: 5.6}}}
+
+ VAR syntax
+ # The VAR syntax supports types the same way as the Variables section
+ VAR ${number: int} 42
+ Should Be Equal ${number} ${42}
+
+ Assignment
+ # In simple cases the VAR syntax is more convenient.
+ ${number: int} = Set Variable 42
+ Should Be Equal ${number} ${42}
+ # In this example conversion is more useful.
+ ${match} ${version: float} = Should Match Regexp RF 7.3 ^RF (\\d+\\.\\d+)$
+ Should Be Equal ${match} RF 7.3
+ Should Be Equal ${version} ${7.3}
+
+ FOR loop
+ FOR ${fib: int} IN 0 1 1 2 3 5 8 13
+ Log ${fib}
+ END
+
+ Keyword arguments
+ # Argument conversion with user keywords is very convenient!
+ Move 10 down slow=no
+ # Conversion handles validation automatically. This usage fails.
+ Move 10 invalid
+
+ Embedded arguments
+ # Also embedded arguments can be converted.
+ Move 3.14 meters
+
+ *** Keywords ***
+ Move
+ [Arguments] ${distance: int} ${direction: Literal["UP", "DOWN"]} ${slow: bool}=True
+ Should Be Equal ${distance} ${10}
+ Should Be Equal ${direction} DOWN
+ Should Be Equal ${slow} ${False}
+
+ Move ${distance: int | float} meters
+ Should Be Equal ${distance} ${3.14}
+
+Fixes and enhancements for timeouts
+-----------------------------------
+
+Several high priority and even critical issues related to timeouts have been fixed.
+Most of them are related to library keywords using `BuiltIn.run_keyword` which is
+a somewhat special case, but some problems occurred also with normal keywords.
+In addition to fixes, there have been some enhancements as well.
+
+Avoid output file corruption
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Library keywords can use `BuiltIn.run_keyword` as an API to execute other keywords.
+If Robot Framework timeouts occurred when that was done, the timeout could interrupt
+Robot Framework's own code that was preparing the new keyword to be executed.
+That situation was otherwise handled fine, but if the timeout occurred when Robot
+Framework was writing information to the output file, the output file could be
+corrupted and it was not possible to generate log and report after the execution.
+This severe problem has now been fixed by automatically pausing timeouts when
+`BuiltIn.run_keyword` is used (`#5417`_).
+
+Normally the odds that a timeout occurred after the parent keyword had called
+`BuiltIn.run_keyword`, but before the child keyword had actually started running,
+were pretty small, but if there were lof of such calls and also if child keywords
+logged lot of messages, the odds grew bigger. It is very likely that some
+of the mysterious problems with output files being corrupted that have been
+reported to our issue tracker have been caused by this issue. Hopefully we get
+less such reports in the future!
+
+Other fixes related to `BuiltIn.run_keyword` and timeouts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are also some other fixes related to library keywords using `BuiltIn.run_keyword`
+when timeouts are enabled:
+
+- Timeouts are not deactivated after the child keyword returns (`#5422`_).
+ This problem occurred only outside Windows and actually prevented the above
+ bug corrupting output files outside Windows as well.
+- Order and position of logged messages is correct (`#5423`_).
+
+Other fixes related to timeouts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Logged messages respect the current log level (`#5395`_).
+- Writing messages to the debug file and to the console is not delayed (`#3644`_).
+
+Timeout related enhancements
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- It was discovered that libraries can easily handle Robot Framework's timeouts
+ so that they can do cleanup activities if needed. How to do that in practice
+ has been now documented in the User Guide (`#5377`_).
+- Timeout support with Dialogs (`#5386`_) and Process (`#5345`_, `#5376`_)
+ libraries has been enhanced. These enhancements are discussed separately below.
+
+Fix crash if library has implemented `__dir__` and `__getattr__`
+----------------------------------------------------------------
+
+Although implementing `__dir__` is pretty rare, hard crashes are always severe.
+As a concrete problem this bug prevented using the Faker tool directly as
+a library (`#5368`_).
+
+Enhancements to the Dialogs library
+-----------------------------------
+
+The Dialogs library is widely used in cases where something cannot be fully
+automated or execution needs to be paused for some reason. It got two major
+enhancements in this release.
+
+Support timeouts and close dialogs with Ctrl-C
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Robot Framework's timeouts are now finally able to kill opened dialogs (`#5386`_).
+Earlier execution hang indefinitely if dialogs were open even if a timeout occurred,
+and the timeout was really activated only after the dialog was manually closed.
+The same fix also makes it possible to stop the execution with Ctrl-C even if
+a dialog would be open.
+
+Enhanced look and feel
+~~~~~~~~~~~~~~~~~~~~~~
+
+The actual dialogs were enhanced in different ways (`#5334`_):
+
+- Dialogs got application and taskbar icons.
+- Font size has been increased a bit to make text easier to read.
+- More padding has been added around elements to make dialogs look better.
+ Buttons being separated from each others a bit more also avoids misclicks.
+- As the result of the above two changes, also the dialog size has increased.
+
+See `this comment`__ for an example how new and old dialogs look like.
+
+__ https://github.com/robotframework/robotframework/issues/5334#issuecomment-2761597900
+
+Enhancements to the Process library
+-----------------------------------
+
+Also the Process library got two major enhancements in this release.
+
+Avoid deadlock if process produces lot of output
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It has been possible to avoid the deadlock by redirecting `stdout` and `stderr`
+to files, but that is normally not necessary anymore (`#4173`_). Redirecting
+outputs to files is often a good idea anyway, and should be done at least if
+a process produces a huge amount of output.
+
+Better support for Robot Framework's timeouts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The Process library has its own timeout mechanism, but it now works better also
+with Robot Framework's test and keyword timeouts:
+
+- Robot Framework's timeouts were earlier not able to interrupt `Run Process` and
+ `Wait For Process` at all on Windows (`#5345`_). In the worst case the execution
+ could hang.
+- Nowadays the process that is waited for is killed if Robot Framework timeout
+ occurs (`#5376`_). This is better than leaving the process running on
+ the background.
+
+Python 3.14 compatibility
+-------------------------
+
+Robot Framework 7.3 is officially compatible with the forthcoming `Python 3.14`__
+release (`#5352`_). No code changes were needed so also older Robot Framework
+versions ought to work fine.
+
+__ https://docs.python.org/3.14/whatsnew/3.14.html
+
+Automatic code formatting
+-------------------------
+
+Robot Framework source code and also test code has been auto-formatted
+(`#5387`_). This is not really an enhancement in the tool itself, but
+automatic formatting makes it easier to create and review pull requests.
+
+Formatting is done using a combination of Ruff__, Black__ and isort__. These
+tools should not be used directly, but instead formatting should be done
+using an invoke__ task like::
+
+ invoke format
+
+More detailed instructions will be written to the `contribution guidelines`__
+in the near future.
+
+__ https://docs.astral.sh/ruff/
+__ https://black.readthedocs.io/en/stable/
+__ https://pycqa.github.io/isort/
+__ https://www.pyinvoke.org/
+__ https://github.com/robotframework/robotframework/blob/master/CONTRIBUTING.rst
+
+Backwards incompatible changes
+==============================
+
+There is only one known backwards incompatible change in this release, but
+`every change can break someones workflow`__.
+
+__ https://xkcd.com/1172/
+
+Variable type syntax may clash with existing variables
+------------------------------------------------------
+
+The syntax to specify variable types like `${x: int}` (`#3278`_) may clash with
+existing variables having names with colons. This is not very likely, though,
+because the type syntax requires having a space after the colon and names like
+`${foo:bar}` are thus not affected. If someone actually has a variable with
+a space after a colon, the space needs to be removed.
+
+Deprecated features
+===================
+
+Deprecated utility functions
+----------------------------
+
+The following functions and other utilities under the `robot.utils` package
+have been deprecated:
+
+- `is_string`, `is_bytes`, `is_number`, `is_integer` and `is_pathlike` have been
+ deprecated and should be replaced with `isinstance` like `isinstance(item, str)`
+ and `isinstance(item, int)` (`#5416`_).
+- `robot.utils.ET` has been deprecated and `xml.etree.ElementTree` should be
+ used instead (`#5415`_).
+
+Various other__ utilities__ have been deprecated in previous releases. Currently
+deprecation warnings related to all these utils are not visible by default,
+but they will be changed to more visible warnings in Robot Framework 8.0 and
+the plan is to remove the utils in Robot Framework 9.0. Use the PYTHONWARNINGS__
+environment variable or Python's `-W`__ option to make warnings more visible
+if you want to see is your tool using any deprecated APIs. For example,
+`-W error` turns all deprecation warnings to exceptions making them very
+easy to discover.
+
+__ https://github.com/robotframework/robotframework/issues/4150
+__ https://github.com/robotframework/robotframework/issues/4500
+__ https://docs.python.org/3/using/cmdline.html#envvar-PYTHONWARNINGS
+__ https://docs.python.org/3/using/cmdline.html#cmdoption-W
+
+Acknowledgements
+================
+
+Robot Framework development is sponsored by the `Robot Framework Foundation`_
+and its over 70 member organizations. If your organization is using Robot Framework
+and benefiting from it, consider joining the foundation to support its
+development as well.
+
+Robot Framework 7.3 team funded by the foundation consisted of `Pekka Klärck`_ and
+`Janne Härkönen `_. Janne worked only part-time and was
+mainly responsible on Libdoc related fixes. In addition to work done by them, the
+community has provided some great contributions:
+
+- `Tatu Aalto `__ worked with Pekka to implement
+ variable type conversion (`#3278`_). That was big task so huge thanks for
+ Tatu and his employer `OP `__ who let Tatu to use his
+ work time for this enhancement.
+
+- `@franzhaas `__ helped with the Process library.
+ He provided initial implementation both for avoiding deadlock (`#4173`_) and
+ for fixing Robot Framework timeout support on Windows (`#5345`_).
+
+- `Olivier Renault `__ fixed a bug with BDD prefixes
+ having same beginning (`#5340`_) and enhanced French BDD prefixes (`#5150`_).
+
+- `Gad Hassine `__ provided Arabic localization (`#5357`_).
+
+- `Lucian D. Crainic `__ added Italian Libdoc UI
+ translation (`#5351`_)
+
+Big thanks to Robot Framework Foundation, to community members listed above, and to
+everyone else who has tested preview releases, submitted bug reports, proposed
+enhancements, debugged problems, or otherwise helped with Robot Framework 7.3
+development.
+
+| `Pekka Klärck `_
+| Robot Framework lead developer
+
+Full list of fixes and enhancements
+===================================
+
+.. list-table::
+ :header-rows: 1
+
+ * - ID
+ - Type
+ - Priority
+ - Summary
+ - Added
+ * - `#5368`_
+ - bug
+ - critical
+ - Library with custom `__dir__` and attributes implemented via `__getattr__` causes crash
+ - rc 1
+ * - `#5417`_
+ - bug
+ - critical
+ - Output file can be corrupted if library keyword uses `BuiltIn.run_keyword` and timeout occurs
+ - rc 1
+ * - `#3278`_
+ - enhancement
+ - critical
+ - Variable type conversion
+ - rc 1
+ * - `#5352`_
+ - enhancement
+ - critical
+ - Python 3.14 compatibility
+ - rc 2
+ * - `#4173`_
+ - bug
+ - high
+ - Process: Avoid deadlock when standard streams are not redirected to files
+ - rc 1
+ * - `#5386`_
+ - bug
+ - high
+ - Dialogs: Not possible to stop execution with timeouts or by pressing Ctrl-C
+ - rc 1
+ * - `#5334`_
+ - enhancement
+ - high
+ - Dialogs: Enhance look and feel
+ - rc 1
+ * - `#5387`_
+ - enhancement
+ - high
+ - Automatic code formatting
+ - rc 1
+ * - `#3644`_
+ - bug
+ - medium
+ - Writing messages to debug file and to console is delayed when timeouts are used
+ - rc 1
+ * - `#5330`_
+ - bug
+ - medium
+ - Keyword accepting embedded arguments cannot be used with variable containing characters used in keyword name
+ - rc 1
+ * - `#5340`_
+ - bug
+ - medium
+ - BDD prefixes with same beginning are not handled properly
+ - rc 1
+ * - `#5345`_
+ - bug
+ - medium
+ - Process: Test and keyword timeouts do not work when running processes on Windows
+ - rc 1
+ * - `#5358`_
+ - bug
+ - medium
+ - Libdoc: TypedDict documentation is broken in HTML output
+ - rc 1
+ * - `#5367`_
+ - bug
+ - medium
+ - Embedded arguments are not passed as objects when executed as setup/teardown
+ - rc 1
+ * - `#5393`_
+ - bug
+ - medium
+ - Cannot use keyword with parameterized special form like `TypeForm[param]` as type hint
+ - rc 1
+ * - `#5394`_
+ - bug
+ - medium
+ - Embedded arguments using custom regexps cannot be used with inline Python evaluation syntax
+ - rc 1
+ * - `#5395`_
+ - bug
+ - medium
+ - Messages logged when timeouts are active do not respect current log level
+ - rc 1
+ * - `#5399`_
+ - bug
+ - medium
+ - TEST scope variable set on suite level removes SUITE scope variable with same name
+ - rc 1
+ * - `#5405`_
+ - bug
+ - medium
+ - Extended variable assignment doesn't work with `@` or `&` syntax
+ - rc 1
+ * - `#5422`_
+ - bug
+ - medium
+ - Timeouts are deactivated if library keyword uses `BuiltIn.run_keyword` (except on Windows)
+ - rc 1
+ * - `#5423`_
+ - bug
+ - medium
+ - Log messages are in wrong order if library keyword uses `BuiltIn.run_keyword` and timeouts are used
+ - rc 1
+ * - `#5433`_
+ - bug
+ - medium
+ - Confusing error messages when adding incompatible objects to `TestSuite` structure
+ - rc 2
+ * - `#5150`_
+ - enhancement
+ - medium
+ - Enhance BDD support (GIVEN/WHEN/THEN) for French language
+ - rc 1
+ * - `#5351`_
+ - enhancement
+ - medium
+ - Add Italian Libdoc UI translation
+ - rc 1
+ * - `#5357`_
+ - enhancement
+ - medium
+ - Add Arabic localization
+ - rc 1
+ * - `#5376`_
+ - enhancement
+ - medium
+ - Process: Kill process if Robot's timeout occurs when waiting for process to end
+ - rc 1
+ * - `#5377`_
+ - enhancement
+ - medium
+ - Document how libraries can do cleanup activities if Robot's timeout occurs
+ - rc 1
+ * - `#5385`_
+ - enhancement
+ - medium
+ - Bundle logo to distribution package and make it available for external tools
+ - rc 1
+ * - `#5412`_
+ - enhancement
+ - medium
+ - Change keywords accepting configuration arguments as `**config` to use named-only arguments instead
+ - rc 1
+ * - `#5414`_
+ - enhancement
+ - medium
+ - Add explicit APIs to `robot` root package and to all sub packages
+ - rc 1
+ * - `#5416`_
+ - enhancement
+ - medium
+ - Deprecate `is_string`, `is_bytes`, `is_number`, `is_integer` and `is_pathlike` utility functions
+ - rc 1
+ * - `#5398`_
+ - bug
+ - low
+ - Variable assignment is not validated during parsing
+ - rc 1
+ * - `#5403`_
+ - bug
+ - low
+ - Confusing error message when using arguments with user keyword having invalid argument specification
+ - rc 1
+ * - `#5404`_
+ - bug
+ - low
+ - Time strings using same marker multiple times like `2 seconds 3 seconds` should be invalid
+ - rc 1
+ * - `#5418`_
+ - bug
+ - low
+ - DateTime: Getting timestamp as epoch seconds fails close to the epoch on Windows
+ - rc 1
+ * - `#5432`_
+ - bug
+ - low
+ - Small bugs in `robot.utils.Importer`
+ - rc 2
+ * - `#5332`_
+ - enhancement
+ - low
+ - Make list of languages in Libdoc's default language selection dynamic
+ - rc 1
+ * - `#5396`_
+ - enhancement
+ - low
+ - Document limitations with embedded arguments utilizing custom regexps with variables
+ - rc 1
+ * - `#5397`_
+ - enhancement
+ - low
+ - Expose execution mode via `${OPTIONS.rpa}`
+ - rc 1
+ * - `#5415`_
+ - enhancement
+ - low
+ - Deprecate `robot.utils.ET` and use `xml.etree.ElementTree` instead
+ - rc 1
+ * - `#5424`_
+ - enhancement
+ - low
+ - Document ERROR level and that logging with it stops execution if `--exit-on-error` is enabled
+ - rc 1
+
+Altogether 41 issues. View on the `issue tracker `__.
+
+.. _#5368: https://github.com/robotframework/robotframework/issues/5368
+.. _#5417: https://github.com/robotframework/robotframework/issues/5417
+.. _#3278: https://github.com/robotframework/robotframework/issues/3278
+.. _#5352: https://github.com/robotframework/robotframework/issues/5352
+.. _#4173: https://github.com/robotframework/robotframework/issues/4173
+.. _#5386: https://github.com/robotframework/robotframework/issues/5386
+.. _#5334: https://github.com/robotframework/robotframework/issues/5334
+.. _#5387: https://github.com/robotframework/robotframework/issues/5387
+.. _#3644: https://github.com/robotframework/robotframework/issues/3644
+.. _#5330: https://github.com/robotframework/robotframework/issues/5330
+.. _#5340: https://github.com/robotframework/robotframework/issues/5340
+.. _#5345: https://github.com/robotframework/robotframework/issues/5345
+.. _#5358: https://github.com/robotframework/robotframework/issues/5358
+.. _#5367: https://github.com/robotframework/robotframework/issues/5367
+.. _#5393: https://github.com/robotframework/robotframework/issues/5393
+.. _#5394: https://github.com/robotframework/robotframework/issues/5394
+.. _#5395: https://github.com/robotframework/robotframework/issues/5395
+.. _#5399: https://github.com/robotframework/robotframework/issues/5399
+.. _#5405: https://github.com/robotframework/robotframework/issues/5405
+.. _#5422: https://github.com/robotframework/robotframework/issues/5422
+.. _#5423: https://github.com/robotframework/robotframework/issues/5423
+.. _#5433: https://github.com/robotframework/robotframework/issues/5433
+.. _#5150: https://github.com/robotframework/robotframework/issues/5150
+.. _#5351: https://github.com/robotframework/robotframework/issues/5351
+.. _#5357: https://github.com/robotframework/robotframework/issues/5357
+.. _#5376: https://github.com/robotframework/robotframework/issues/5376
+.. _#5377: https://github.com/robotframework/robotframework/issues/5377
+.. _#5385: https://github.com/robotframework/robotframework/issues/5385
+.. _#5412: https://github.com/robotframework/robotframework/issues/5412
+.. _#5414: https://github.com/robotframework/robotframework/issues/5414
+.. _#5416: https://github.com/robotframework/robotframework/issues/5416
+.. _#5398: https://github.com/robotframework/robotframework/issues/5398
+.. _#5403: https://github.com/robotframework/robotframework/issues/5403
+.. _#5404: https://github.com/robotframework/robotframework/issues/5404
+.. _#5418: https://github.com/robotframework/robotframework/issues/5418
+.. _#5432: https://github.com/robotframework/robotframework/issues/5432
+.. _#5332: https://github.com/robotframework/robotframework/issues/5332
+.. _#5396: https://github.com/robotframework/robotframework/issues/5396
+.. _#5397: https://github.com/robotframework/robotframework/issues/5397
+.. _#5415: https://github.com/robotframework/robotframework/issues/5415
+.. _#5424: https://github.com/robotframework/robotframework/issues/5424
diff --git a/doc/releasenotes/rf-7.3rc3.rst b/doc/releasenotes/rf-7.3rc3.rst
new file mode 100644
index 00000000000..1c45fe4316b
--- /dev/null
+++ b/doc/releasenotes/rf-7.3rc3.rst
@@ -0,0 +1,686 @@
+=======================================
+Robot Framework 7.3 release candidate 3
+=======================================
+
+.. default-role:: code
+
+`Robot Framework`_ 7.3 is a feature release with variable type conversion,
+enhancements and fixes related to timeouts, official Python 3.14 compatibility
+and various other exciting new features and high priority bug fixes. This
+release candidate contains all planned code changes.
+
+Questions and comments related to the release can be sent to the `#devel`
+channel on `Robot Framework Slack`_ and possible bugs submitted to
+the `issue tracker`_.
+
+If you have pip_ installed, just run
+
+::
+
+ pip install --pre --upgrade robotframework
+
+to install the latest available release or use
+
+::
+
+ pip install robotframework==7.3rc3
+
+to install exactly this version. Alternatively you can download the package
+from PyPI_ and install it manually. For more details and other installation
+approaches, see the `installation instructions`_.
+
+Robot Framework 7.3 rc 3 was released on Wednesday May 21, 2025. Compared to the
+`second release candidate `_, it mainly contains support for
+variable conversion also from the command line and some more bug fixes.
+The final release is targeted for Tuesday May 27, 2025.
+
+.. _Robot Framework: http://robotframework.org
+.. _Robot Framework Foundation: http://robotframework.org/foundation
+.. _pip: http://pip-installer.org
+.. _PyPI: https://pypi.python.org/pypi/robotframework
+.. _issue tracker milestone: https://github.com/robotframework/robotframework/issues?q=milestone%3Av7.3
+.. _issue tracker: https://github.com/robotframework/robotframework/issues
+.. _robotframework-users: http://groups.google.com/group/robotframework-users
+.. _Slack: http://slack.robotframework.org
+.. _Robot Framework Slack: Slack_
+.. _installation instructions: ../../INSTALL.rst
+
+.. contents::
+ :depth: 2
+ :local:
+
+Most important enhancements
+===========================
+
+Variable type conversion
+------------------------
+
+The most important new feature in Robot Framework 7.3 is variable type conversion
+in the data (`#3278`_) and with the command line variables (`#2946`_). The syntax
+to specify variable types is `${name: type}` in the data and `name: type:value`
+on the command line, and the space after the colon is mandatory in both cases.
+Variable type conversion supports the same types that the `argument conversion`__
+supports. For example, `${number: int}` means that the value of the variable
+`${number}` is converted to an integer.
+
+__ http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#supported-conversions
+
+Variable conversion in data
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Variable types work in the Variables section, with the `VAR` syntax, when creating
+variables based on keyword return values, with FOR loops and, very importantly, with
+user keyword arguments. All these usages are demonstrated by the following examples:
+
+.. sourcecode:: robotframework
+
+ *** Variables ***
+ # Simple type.
+ ${VERSION: float} 7.3
+ # Parameterized type.
+ ${CRITICAL: list[int]} [3278, 5368, 5417]
+ # With @{list} variables the type specified the item type.
+ @{HIGH: int} 4173 5334 5386 5387
+ # With @{dict} variables the type specified the value type.
+ &{DATES: date} rc1=2025-05-08 final=2025-05-15
+ # Alternative syntax to specify both key and value types.
+ &{NUMBERS: int=float} 1=2.3 4=5.6
+
+ *** Test Cases ***
+ Variables section
+ # Validate above variables using the inline Python evaluation syntax.
+ # This syntax is much more complicated than the syntax used above!
+ Should Be Equal ${VERSION} ${{7.3}}
+ Should Be Equal ${CRITICAL} ${{[3278, 5368, 5417]}}
+ Should Be Equal ${HIGH} ${{[4173, 5334, 5386, 5387]}}
+ Should Be Equal ${DATES} ${{{'rc1': datetime.date(2025, 5, 8), 'final': datetime.date(2025, 5, 15)}}}
+ Should Be Equal ${NUMBERS} ${{{1: 2.3, 4: 5.6}}}
+
+ VAR syntax
+ # The VAR syntax supports types the same way as the Variables section
+ VAR ${number: int} 42
+ Should Be Equal ${number} ${42}
+
+ Assignment
+ # In simple cases the VAR syntax is more convenient.
+ ${number: int} = Set Variable 42
+ Should Be Equal ${number} ${42}
+ # In this example conversion is more useful.
+ ${match} ${version: float} = Should Match Regexp RF 7.3 ^RF (\\d+\\.\\d+)$
+ Should Be Equal ${match} RF 7.3
+ Should Be Equal ${version} ${7.3}
+
+ FOR loop
+ FOR ${fib: int} IN 0 1 1 2 3 5 8 13
+ Log ${fib}
+ END
+
+ Keyword arguments
+ # Argument conversion with user keywords is very convenient!
+ Move 10 down slow=no
+ # Conversion handles validation automatically. This usage fails.
+ Move 10 invalid
+
+ Embedded arguments
+ # Also embedded arguments can be converted.
+ Move 3.14 meters
+
+ *** Keywords ***
+ Move
+ [Arguments] ${distance: int} ${direction: Literal["UP", "DOWN"]} ${slow: bool}=True
+ Should Be Equal ${distance} ${10}
+ Should Be Equal ${direction} DOWN
+ Should Be Equal ${slow} ${False}
+
+ Move ${distance: int | float} meters
+ Should Be Equal ${distance} ${3.14}
+
+Variable conversion on command line
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Variable conversion works also with variables given from the command line using
+the `--variable` option. The syntax is `name: type:value` and, due to the space
+being mandatory, the whole option value typically needs to be quoted. Following
+examples demonstrate some possible usages for this functionality::
+
+ --variable "ITERATIONS: int:99"
+ --variable "PAYLOAD: dict:{'id': 1, 'name': 'Robot'}"
+ --variable "START_TIME: datetime:now"
+
+Notice that the last conversion uses the new `datetime` conversion that allows
+getting the current local date and time with the special value `now` (`#5440`_).
+
+Fixes and enhancements for timeouts
+-----------------------------------
+
+Several high priority and even critical issues related to timeouts have been fixed.
+Most of them are related to library keywords using `BuiltIn.run_keyword` which is
+a somewhat special case, but some problems occurred also with normal keywords.
+In addition to fixes, there have been some enhancements as well.
+
+Avoid output file corruption
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Library keywords can use `BuiltIn.run_keyword` as an API to execute other keywords.
+If Robot Framework timeouts occurred when that was done, the timeout could interrupt
+Robot Framework's own code that was preparing the new keyword to be executed.
+That situation was otherwise handled fine, but if the timeout occurred when Robot
+Framework was writing information to the output file, the output file could be
+corrupted and it was not possible to generate log and report after the execution.
+This severe problem has now been fixed by automatically pausing timeouts when
+`BuiltIn.run_keyword` is used (`#5417`_).
+
+Normally the odds that a timeout occurred after the parent keyword had called
+`BuiltIn.run_keyword`, but before the child keyword had actually started running,
+were pretty small, but if there were lof of such calls and also if child keywords
+logged lot of messages, the odds grew bigger. It is very likely that some
+of the mysterious problems with output files being corrupted that have been
+reported to our issue tracker have been caused by this issue. Hopefully we get
+less such reports in the future!
+
+Other fixes related to `BuiltIn.run_keyword` and timeouts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are also some other fixes related to library keywords using `BuiltIn.run_keyword`
+when timeouts are enabled:
+
+- Timeouts are not deactivated after the child keyword returns (`#5422`_).
+ This problem occurred only outside Windows and actually prevented the above
+ bug corrupting output files outside Windows as well.
+- Order and position of logged messages is correct (`#5423`_).
+
+Other fixes related to timeouts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Logged messages respect the current log level (`#5395`_).
+- Writing messages to the debug file and to the console is not delayed (`#3644`_).
+
+Timeout related enhancements
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- It was discovered that libraries can easily handle Robot Framework's timeouts
+ so that they can do cleanup activities if needed. How to do that in practice
+ has been now documented in the User Guide (`#5377`_).
+- Timeout support with Dialogs (`#5386`_) and Process (`#5345`_, `#5376`_)
+ libraries has been enhanced. These enhancements are discussed separately below.
+
+Fix crash if library has implemented `__dir__` and `__getattr__`
+----------------------------------------------------------------
+
+Although implementing `__dir__` is pretty rare, hard crashes are always severe.
+As a concrete problem this bug prevented using the Faker tool directly as
+a library (`#5368`_).
+
+Enhancements to the Dialogs library
+-----------------------------------
+
+The Dialogs library is widely used in cases where something cannot be fully
+automated or execution needs to be paused for some reason. It got two major
+enhancements in this release.
+
+Support timeouts and close dialogs with Ctrl-C
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Robot Framework's timeouts are now finally able to kill opened dialogs (`#5386`_).
+Earlier execution hang indefinitely if dialogs were open even if a timeout occurred,
+and the timeout was really activated only after the dialog was manually closed.
+The same fix also makes it possible to stop the execution with Ctrl-C even if
+a dialog would be open.
+
+Enhanced look and feel
+~~~~~~~~~~~~~~~~~~~~~~
+
+The actual dialogs were enhanced in different ways (`#5334`_):
+
+- Dialogs got application and taskbar icons.
+- Font size has been increased a bit to make text easier to read.
+- More padding has been added around elements to make dialogs look better.
+ Buttons being separated from each others a bit more also avoids misclicks.
+- As the result of the above two changes, also the dialog size has increased.
+
+See `this comment`__ for an example how new and old dialogs look like.
+
+__ https://github.com/robotframework/robotframework/issues/5334#issuecomment-2761597900
+
+Enhancements to the Process library
+-----------------------------------
+
+Also the Process library got two major enhancements in this release.
+
+Avoid deadlock if process produces lot of output
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It has been possible to avoid the deadlock by redirecting `stdout` and `stderr`
+to files, but that is normally not necessary anymore (`#4173`_). Redirecting
+outputs to files is often a good idea anyway, and should be done at least if
+a process produces a huge amount of output.
+
+Better support for Robot Framework's timeouts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The Process library has its own timeout mechanism, but it now works better also
+with Robot Framework's test and keyword timeouts:
+
+- Robot Framework's timeouts were earlier not able to interrupt `Run Process` and
+ `Wait For Process` at all on Windows (`#5345`_). In the worst case the execution
+ could hang.
+- Nowadays the process that is waited for is killed if Robot Framework timeout
+ occurs (`#5376`_). This is better than leaving the process running on
+ the background.
+
+Python 3.14 compatibility
+-------------------------
+
+Robot Framework 7.3 is officially compatible with the forthcoming `Python 3.14`__
+release (`#5352`_). No code changes were needed so also older Robot Framework
+versions ought to work fine.
+
+__ https://docs.python.org/3.14/whatsnew/3.14.html
+
+Automatic code formatting
+-------------------------
+
+Robot Framework source code and also test code has been auto-formatted
+(`#5387`_). This is not really an enhancement in the tool itself, but
+automatic formatting makes it easier to create and review pull requests.
+
+Formatting is done using a combination of Ruff__, Black__ and isort__. These
+tools should not be used directly, but instead formatting should be done
+using an invoke__ task like::
+
+ invoke format
+
+More detailed instructions will be written to the `contribution guidelines`__
+in the near future.
+
+__ https://docs.astral.sh/ruff/
+__ https://black.readthedocs.io/en/stable/
+__ https://pycqa.github.io/isort/
+__ https://www.pyinvoke.org/
+__ https://github.com/robotframework/robotframework/blob/master/CONTRIBUTING.rst
+
+Backwards incompatible changes
+==============================
+
+All known backwards incompatible changes in this release are related to
+the variable conversion syntax, but `every change can break someones workflow`__
+so we recommend everyone to test this release before using it in production.
+
+__ https://xkcd.com/1172/
+
+Variable type syntax in data may clash with existing variables
+--------------------------------------------------------------
+
+The syntax to specify variable types in the data like `${x: int}` (`#3278`_)
+may clash with existing variables having names with colons. This is not very
+likely, though, because the type syntax requires having a space after the colon
+and names like `${x:int}` are thus not affected. If someone actually has
+a variable with a space after a colon, the space needs to be removed.
+
+Command line variable type syntax may clash with existing values
+----------------------------------------------------------------
+
+The variable type syntax can cause problems also with variables given from
+the command line (`#2946`_). Also the syntax to specify variables without a type
+uses a colon like `--variable NAME:value`, but because the type syntax requires
+a space after the colon like `--variable X: int:42`, there typically are no
+problems. In practice there are problems only if a value starts with a space and
+contains one or more colons::
+
+ --variable NAME: this is :not: common
+
+In such cases an explicit type needs to be added::
+
+ --variable NAME: str: this is :not: common
+
+Deprecated features
+===================
+
+Deprecated utility functions
+----------------------------
+
+The following functions and other utilities under the `robot.utils` package
+have been deprecated:
+
+- `is_string`, `is_bytes`, `is_number`, `is_integer` and `is_pathlike` have been
+ deprecated and should be replaced with `isinstance` like `isinstance(item, str)`
+ and `isinstance(item, int)` (`#5416`_).
+- `robot.utils.ET` has been deprecated and `xml.etree.ElementTree` should be
+ used instead (`#5415`_).
+
+Various other__ utilities__ have been deprecated in previous releases. Currently
+deprecation warnings related to all these utils are not visible by default,
+but they will be changed to more visible warnings in Robot Framework 8.0 and
+the plan is to remove the utils in Robot Framework 9.0. Use the PYTHONWARNINGS__
+environment variable or Python's `-W`__ option to make warnings more visible
+if you want to see is your tool using any deprecated APIs. For example,
+`-W error` turns all deprecation warnings to exceptions making them very
+easy to discover.
+
+__ https://github.com/robotframework/robotframework/issues/4150
+__ https://github.com/robotframework/robotframework/issues/4500
+__ https://docs.python.org/3/using/cmdline.html#envvar-PYTHONWARNINGS
+__ https://docs.python.org/3/using/cmdline.html#cmdoption-W
+
+Acknowledgements
+================
+
+Robot Framework is developed with support from the Robot Framework Foundation
+and its 80+ member organizations. Join the journey — support the project by
+`joining the Foundation `_.
+
+Robot Framework 7.3 team funded by the foundation consisted of `Pekka Klärck`_ and
+`Janne Härkönen `_. Janne worked only part-time and was
+mainly responsible on Libdoc related fixes. In addition to work done by them, the
+community has provided some great contributions:
+
+- `Tatu Aalto `__ worked with Pekka to implement
+ variable type conversion (`#3278`_), the biggest new feature in this release.
+ Huge thanks to Tatu and to his employer `OP `__, a member
+ of the `Robot Framework Foundation`_, for dedicating work time to make this
+ happen!
+
+- `@franzhaas `__ helped with the Process library.
+ He provided initial implementation both for avoiding deadlock (`#4173`_) and
+ for fixing Robot Framework timeout support on Windows (`#5345`_).
+
+- `Olivier Renault `__ fixed a bug with BDD prefixes
+ having same beginning (`#5340`_) and enhanced French BDD prefixes (`#5150`_).
+
+- `Gad Hassine `__ provided Arabic localization (`#5357`_).
+
+- `Lucian D. Crainic `__ added Italian Libdoc UI
+ translation (`#5351`_)
+
+Big thanks to Robot Framework Foundation, to community members listed above, and
+to everyone else who has tested preview releases, submitted bug reports, proposed
+enhancements, debugged problems, or otherwise helped with Robot Framework 7.3
+development.
+
+| `Pekka Klärck `_
+| Robot Framework lead developer
+
+Full list of fixes and enhancements
+===================================
+
+.. list-table::
+ :header-rows: 1
+
+ * - ID
+ - Type
+ - Priority
+ - Summary
+ - Added
+ * - `#5368`_
+ - bug
+ - critical
+ - Library with custom `__dir__` and attributes implemented via `__getattr__` causes crash
+ - rc 1
+ * - `#5417`_
+ - bug
+ - critical
+ - Output file can be corrupted if library keyword uses `BuiltIn.run_keyword` and timeout occurs
+ - rc 1
+ * - `#3278`_
+ - enhancement
+ - critical
+ - Variable type conversion
+ - rc 1
+ * - `#5352`_
+ - enhancement
+ - critical
+ - Python 3.14 compatibility
+ - rc 2
+ * - `#4173`_
+ - bug
+ - high
+ - Process: Avoid deadlock when standard streams are not redirected to files
+ - rc 1
+ * - `#5386`_
+ - bug
+ - high
+ - Dialogs: Not possible to stop execution with timeouts or by pressing Ctrl-C
+ - rc 1
+ * - `#2946`_
+ - enhancement
+ - high
+ - Variable type conversion with command line variables
+ - rc 3
+ * - `#5334`_
+ - enhancement
+ - high
+ - Dialogs: Enhance look and feel
+ - rc 1
+ * - `#5387`_
+ - enhancement
+ - high
+ - Automatic code formatting
+ - rc 1
+ * - `#3644`_
+ - bug
+ - medium
+ - Writing messages to debug file and to console is delayed when timeouts are used
+ - rc 1
+ * - `#4514`_
+ - bug
+ - medium
+ - Cannot interrupt `robot.run` or `robot.run_cli` and call it again
+ - rc 3
+ * - `#5098`_
+ - bug
+ - medium
+ - `buildout` cannot create start-up scripts using current entry point configuration
+ - rc 3
+ * - `#5330`_
+ - bug
+ - medium
+ - Keyword accepting embedded arguments cannot be used with variable containing characters used in keyword name
+ - rc 1
+ * - `#5340`_
+ - bug
+ - medium
+ - BDD prefixes with same beginning are not handled properly
+ - rc 1
+ * - `#5345`_
+ - bug
+ - medium
+ - Process: Test and keyword timeouts do not work when running processes on Windows
+ - rc 1
+ * - `#5358`_
+ - bug
+ - medium
+ - Libdoc: TypedDict documentation is broken in HTML output
+ - rc 1
+ * - `#5367`_
+ - bug
+ - medium
+ - Embedded arguments are not passed as objects when executed as setup/teardown
+ - rc 1
+ * - `#5393`_
+ - bug
+ - medium
+ - Cannot use keyword with parameterized special form like `TypeForm[param]` as type hint
+ - rc 1
+ * - `#5394`_
+ - bug
+ - medium
+ - Embedded arguments using custom regexps cannot be used with inline Python evaluation syntax
+ - rc 1
+ * - `#5395`_
+ - bug
+ - medium
+ - Messages logged when timeouts are active do not respect current log level
+ - rc 1
+ * - `#5399`_
+ - bug
+ - medium
+ - TEST scope variable set on suite level removes SUITE scope variable with same name
+ - rc 1
+ * - `#5405`_
+ - bug
+ - medium
+ - Extended variable assignment doesn't work with `@` or `&` syntax
+ - rc 1
+ * - `#5422`_
+ - bug
+ - medium
+ - Timeouts are deactivated if library keyword uses `BuiltIn.run_keyword` (except on Windows)
+ - rc 1
+ * - `#5423`_
+ - bug
+ - medium
+ - Log messages are in wrong order if library keyword uses `BuiltIn.run_keyword` and timeouts are used
+ - rc 1
+ * - `#5433`_
+ - bug
+ - medium
+ - Confusing error messages when adding incompatible objects to `TestSuite` structure
+ - rc 2
+ * - `#5150`_
+ - enhancement
+ - medium
+ - Enhance BDD support (GIVEN/WHEN/THEN) for French language
+ - rc 1
+ * - `#5351`_
+ - enhancement
+ - medium
+ - Add Italian Libdoc UI translation
+ - rc 1
+ * - `#5357`_
+ - enhancement
+ - medium
+ - Add Arabic localization
+ - rc 1
+ * - `#5376`_
+ - enhancement
+ - medium
+ - Process: Kill process if Robot's timeout occurs when waiting for process to end
+ - rc 1
+ * - `#5377`_
+ - enhancement
+ - medium
+ - Document how libraries can do cleanup activities if Robot's timeout occurs
+ - rc 1
+ * - `#5385`_
+ - enhancement
+ - medium
+ - Bundle logo to distribution package and make it available for external tools
+ - rc 1
+ * - `#5412`_
+ - enhancement
+ - medium
+ - Change keywords accepting configuration arguments as `**config` to use named-only arguments instead
+ - rc 1
+ * - `#5414`_
+ - enhancement
+ - medium
+ - Add explicit APIs to `robot` root package and to all sub packages
+ - rc 1
+ * - `#5416`_
+ - enhancement
+ - medium
+ - Deprecate `is_string`, `is_bytes`, `is_number`, `is_integer` and `is_pathlike` utility functions
+ - rc 1
+ * - `#5440`_
+ - enhancement
+ - medium
+ - Support `now` and `today` as special values in `datetime` and `date` conversion, respectively
+ - rc 3
+ * - `#5398`_
+ - bug
+ - low
+ - Variable assignment is not validated during parsing
+ - rc 1
+ * - `#5403`_
+ - bug
+ - low
+ - Confusing error message when using arguments with user keyword having invalid argument specification
+ - rc 1
+ * - `#5404`_
+ - bug
+ - low
+ - Time strings using same marker multiple times like `2 seconds 3 seconds` should be invalid
+ - rc 1
+ * - `#5418`_
+ - bug
+ - low
+ - DateTime: Getting timestamp as epoch seconds fails close to the epoch on Windows
+ - rc 1
+ * - `#5432`_
+ - bug
+ - low
+ - Small bugs in `robot.utils.Importer`
+ - rc 2
+ * - `#5332`_
+ - enhancement
+ - low
+ - Make list of languages in Libdoc's default language selection dynamic
+ - rc 1
+ * - `#5396`_
+ - enhancement
+ - low
+ - Document limitations with embedded arguments utilizing custom regexps with variables
+ - rc 1
+ * - `#5397`_
+ - enhancement
+ - low
+ - Expose execution mode via `${OPTIONS.rpa}`
+ - rc 1
+ * - `#5415`_
+ - enhancement
+ - low
+ - Deprecate `robot.utils.ET` and use `xml.etree.ElementTree` instead
+ - rc 1
+ * - `#5424`_
+ - enhancement
+ - low
+ - Document ERROR level and that logging with it stops execution if `--exit-on-error` is enabled
+ - rc 1
+
+Altogether 45 issues. View on the `issue tracker `__.
+
+.. _#5368: https://github.com/robotframework/robotframework/issues/5368
+.. _#5417: https://github.com/robotframework/robotframework/issues/5417
+.. _#3278: https://github.com/robotframework/robotframework/issues/3278
+.. _#5352: https://github.com/robotframework/robotframework/issues/5352
+.. _#4173: https://github.com/robotframework/robotframework/issues/4173
+.. _#5386: https://github.com/robotframework/robotframework/issues/5386
+.. _#2946: https://github.com/robotframework/robotframework/issues/2946
+.. _#5334: https://github.com/robotframework/robotframework/issues/5334
+.. _#5387: https://github.com/robotframework/robotframework/issues/5387
+.. _#3644: https://github.com/robotframework/robotframework/issues/3644
+.. _#4514: https://github.com/robotframework/robotframework/issues/4514
+.. _#5098: https://github.com/robotframework/robotframework/issues/5098
+.. _#5330: https://github.com/robotframework/robotframework/issues/5330
+.. _#5340: https://github.com/robotframework/robotframework/issues/5340
+.. _#5345: https://github.com/robotframework/robotframework/issues/5345
+.. _#5358: https://github.com/robotframework/robotframework/issues/5358
+.. _#5367: https://github.com/robotframework/robotframework/issues/5367
+.. _#5393: https://github.com/robotframework/robotframework/issues/5393
+.. _#5394: https://github.com/robotframework/robotframework/issues/5394
+.. _#5395: https://github.com/robotframework/robotframework/issues/5395
+.. _#5399: https://github.com/robotframework/robotframework/issues/5399
+.. _#5405: https://github.com/robotframework/robotframework/issues/5405
+.. _#5422: https://github.com/robotframework/robotframework/issues/5422
+.. _#5423: https://github.com/robotframework/robotframework/issues/5423
+.. _#5433: https://github.com/robotframework/robotframework/issues/5433
+.. _#5150: https://github.com/robotframework/robotframework/issues/5150
+.. _#5351: https://github.com/robotframework/robotframework/issues/5351
+.. _#5357: https://github.com/robotframework/robotframework/issues/5357
+.. _#5376: https://github.com/robotframework/robotframework/issues/5376
+.. _#5377: https://github.com/robotframework/robotframework/issues/5377
+.. _#5385: https://github.com/robotframework/robotframework/issues/5385
+.. _#5412: https://github.com/robotframework/robotframework/issues/5412
+.. _#5414: https://github.com/robotframework/robotframework/issues/5414
+.. _#5416: https://github.com/robotframework/robotframework/issues/5416
+.. _#5440: https://github.com/robotframework/robotframework/issues/5440
+.. _#5398: https://github.com/robotframework/robotframework/issues/5398
+.. _#5403: https://github.com/robotframework/robotframework/issues/5403
+.. _#5404: https://github.com/robotframework/robotframework/issues/5404
+.. _#5418: https://github.com/robotframework/robotframework/issues/5418
+.. _#5432: https://github.com/robotframework/robotframework/issues/5432
+.. _#5332: https://github.com/robotframework/robotframework/issues/5332
+.. _#5396: https://github.com/robotframework/robotframework/issues/5396
+.. _#5397: https://github.com/robotframework/robotframework/issues/5397
+.. _#5415: https://github.com/robotframework/robotframework/issues/5415
+.. _#5424: https://github.com/robotframework/robotframework/issues/5424
diff --git a/doc/schema/README.rst b/doc/schema/README.rst
index 85b720ca6d5..58e6b07d708 100644
--- a/doc/schema/README.rst
+++ b/doc/schema/README.rst
@@ -12,9 +12,10 @@ at the top of the page.
Schema files
------------
-- ``_ - Robot Framework XML output schema in XSD_ format.
-- ``_ - `JSON Schema`_ for ``robot.running.TestSuite`` model structure.
-- ``_ - `JSON Schema`_ for ``robot.result.TestSuite`` model structure.
+- ``_ - XML result (output.xml) schema in XSD_ format.
+- ``_ - JSON result (output.json) schema in `JSON Schema`_ format.
+- ``_ - `JSON Schema`_ for ``robot.result.TestSuite``.
+- ``_ - `JSON Schema`_ for ``robot.running.TestSuite``.
- ``_ - Libdoc XML spec schema in XSD_ format.
- ``_ - Libdoc JSON spec schema in `JSON Schema`_ format.
@@ -36,8 +37,7 @@ XSD schemas are created by hand and updates need to be done directly to them.
JSON schemas are generated based on models created using pydantic_.
To modify these schemas, first update the appropriate pydantic model either
in ``_, ``_, or ``_
-and then execute that file to regenerate the actual schema file in
-`