From 98190ab003c39404673061d77ce829787c6f26ee Mon Sep 17 00:00:00 2001 From: Lakitna Date: Sat, 4 Apr 2026 11:15:11 +0200 Subject: [PATCH 01/11] Added pick strategy pabot --- src/CacheLibrary/CacheLibrary.py | 19 +++++-- test/integration/run-multi-value.robot | 69 ++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 3 deletions(-) diff --git a/src/CacheLibrary/CacheLibrary.py b/src/CacheLibrary/CacheLibrary.py index 2d48b3e..8b23efa 100644 --- a/src/CacheLibrary/CacheLibrary.py +++ b/src/CacheLibrary/CacheLibrary.py @@ -2,7 +2,7 @@ from contextlib import contextmanager from datetime import datetime, timedelta from pathlib import Path -from typing import Any, Literal, TypeAlias +from typing import Any, Literal, TypeAlias, cast from pabot.pabotlib import PabotLib from robot.api import logger @@ -158,7 +158,7 @@ def cache_retrieve_value(self, key: CacheKey) -> CacheValue | None: def cache_retrieve_value_from_collection( self, key: CacheKey, - pick: Literal["first", "last", "random"] = "first", + pick: Literal["first", "last", "random", "pabot"] = "first", remove_value: bool = True, # noqa: FBT001, FBT002 ) -> CacheValue | None: """ @@ -211,8 +211,12 @@ def cache_retrieve_value_from_collection( index = -1 elif pick == "random": index = random.randint(0, len(values) - 1) # noqa: S311 + elif pick == "pabot": + index = self._pick_strategy_pabot() else: - msg = f"Unexpected pick '{pick}'. Expected one of 'first', 'last', or 'random'." + msg = ( + f"Unexpected pick '{pick}'. Expected one of 'first', 'last', 'random', or 'pabot'." + ) raise ValueError(msg) value = values[index] @@ -221,6 +225,15 @@ def cache_retrieve_value_from_collection( return value + def _pick_strategy_pabot(self) -> int: + index = BuiltIn().get_variable_value("${PABOTEXECUTIONPOOLID}", "abc") + try: + index = int(cast(int, index)) + except: # noqa: E722 + index = 0 + + return index + @keyword(tags=["value"]) def cache_store_value( self, diff --git a/test/integration/run-multi-value.robot b/test/integration/run-multi-value.robot index 62a87d0..727eaa3 100644 --- a/test/integration/run-multi-value.robot +++ b/test/integration/run-multi-value.robot @@ -32,6 +32,23 @@ ${ITERATIONS} 50 ... float ... list ... dict +@{FIXED_INCREMENTAL_COLLECTION} +... 111 +... 222 +... 333 +... 444 +... 555 +... 666 +... 777 +... 888 +... 999 +... 000 +... aaa +... bbb +... ccc +... ddd +... eee +... fff *** Test Cases *** @@ -232,6 +249,36 @@ Store and retrieve random list data Should Be Equal ${retrieved} ${value_set}[${i}] END +Pick strategy Pabot - thread 1 + [Setup] Acquire Lock pick_strat_pabot + [Template] Test Template Pabot Pick Strategy + ${None} + [Teardown] Release Lock pick_strat_pabot + +Pick strategy Pabot - thread 2 + [Setup] Acquire Lock pick_strat_pabot + [Template] Test Template Pabot Pick Strategy + ${None} + [Teardown] Release Lock pick_strat_pabot + +Pick strategy Pabot - thread 3 + [Setup] Acquire Lock pick_strat_pabot + [Template] Test Template Pabot Pick Strategy + ${None} + [Teardown] Release Lock pick_strat_pabot + +Pick strategy Pabot - thread 4 + [Setup] Acquire Lock pick_strat_pabot + [Template] Test Template Pabot Pick Strategy + ${None} + [Teardown] Release Lock pick_strat_pabot + +Pick strategy Pabot - thread 5 + [Setup] Acquire Lock pick_strat_pabot + [Template] Test Template Pabot Pick Strategy + ${None} + [Teardown] Release Lock pick_strat_pabot + *** Keywords *** Generate Complex Collection @@ -269,3 +316,25 @@ Generate Complex Collection END RETURN ${collection} + +Test Template Pabot Pick Strategy + [Arguments] ${_} + Cache Store Collection fixed-strings @{FIXED_INCREMENTAL_COLLECTION} + ${pabot_thread_id} = Get Variable Value ${PABOTEXECUTIONPOOLID} 0 + ${expected_value} = Get From List ${FIXED_INCREMENTAL_COLLECTION} ${pabot_thread_id} + + ${other_tread_id} = Get Parallel Value For Key pick_strat_pabot_thread + ${other_tread_expected_value} = Get Parallel Value For Key pick_strat_pabot_value + IF '${other_tread_expected_value}' == '${EMPTY}' + Set Parallel Value For Key pick_strat_pabot_thread ${pabot_thread_id} + Set Parallel Value For Key pick_strat_pabot_value ${expected_value} + ELSE IF ${pabot_thread_id} != ${other_tread_id} + Should Not Be Equal As Strings ${expected_value} ${other_tread_expected_value} + ELSE + Should Be Equal As Strings ${expected_value} ${other_tread_expected_value} + END + + FOR ${_} IN RANGE ${5} + ${retrieved} = Cache Retrieve Value From Collection fixed-strings pick=pabot remove_value=False + Should Be Equal ${retrieved} ${expected_value} + END From 62025fed42b07b811c54a3295df872ed444e5e79 Mon Sep 17 00:00:00 2001 From: Lakitna Date: Tue, 7 Apr 2026 10:06:51 +0200 Subject: [PATCH 02/11] Set default pabot execution pool id to 0 --- src/CacheLibrary/CacheLibrary.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CacheLibrary/CacheLibrary.py b/src/CacheLibrary/CacheLibrary.py index 8b23efa..11b8360 100644 --- a/src/CacheLibrary/CacheLibrary.py +++ b/src/CacheLibrary/CacheLibrary.py @@ -226,9 +226,9 @@ def cache_retrieve_value_from_collection( return value def _pick_strategy_pabot(self) -> int: - index = BuiltIn().get_variable_value("${PABOTEXECUTIONPOOLID}", "abc") + index = BuiltIn().get_variable_value("${PABOTEXECUTIONPOOLID}", 0) try: - index = int(cast(int, index)) + index = int(cast(str | int, index)) except: # noqa: E722 index = 0 From 280beb92da6de09eb53ac7dd6f5591c21e63bf5e Mon Sep 17 00:00:00 2001 From: Lakitna Date: Tue, 7 Apr 2026 10:35:23 +0200 Subject: [PATCH 03/11] Updated docstring --- src/CacheLibrary/CacheLibrary.py | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/CacheLibrary/CacheLibrary.py b/src/CacheLibrary/CacheLibrary.py index 11b8360..97f9b03 100644 --- a/src/CacheLibrary/CacheLibrary.py +++ b/src/CacheLibrary/CacheLibrary.py @@ -167,9 +167,9 @@ def cache_retrieve_value_from_collection( Will return a single value from a collection stored in the cache, or `None` if there is no value. - | `key` | Name of the collection | - | `pick=first` | How to pick a value from the collection. Can be 'first', 'last', or 'random' | - | `remove_value=True` | Should the value be removed from the collection | + | `key` | Name of the collection | + | `pick=first` | How to pick a value from the collection. Can be 'first', 'last', 'random', or 'pabot' | + | `remove_value=True` | Should the value be removed from the collection | = Examples = @@ -177,6 +177,9 @@ def cache_retrieve_value_from_collection( Retrieve the first value from a cached collection. + The value is automatically removed from the collection. Because of this, you will receive a + new value every time you use this keyword. + | ${user} = Cache Retrieve Value From Collection user-accounts -------------------- @@ -187,6 +190,26 @@ def cache_retrieve_value_from_collection( collection. | ${user} = Cache Retrieve Value From Collection user-accounts pick=random remove_value=${False} + + -------------------- + + == Pick pabot process ID == + + Always retrieve the same value within a pabot process. This ensures that a value is only + used once at the same moment in time. The value will be used multiple times, just not at + the same time. + + For example, this could be useful when testing an application where a user can only be + logged in once. If you log in with two browsers at the same time, the user is logged out in + the first browser. Using the `pabot` pick option here will ensure that: + + - A user is only logged in once. It's never used in multiple tests at the same time. + - A user is used for multiple tests. + + This works with and without pabot. When you run without pabot, it will pick the first value + in the collection. + + | ${user} = Cache Retrieve Value From Collection user-accounts pick=pabot remove_value=${False} """ # noqa: E501 cache = self.cache_file.get() cache = self._ensure_complete_cache(cache)["COLLECTION"] From e404e1e6c39f3663963d451ce95d74ae73f1c6fa Mon Sep 17 00:00:00 2001 From: Lakitna Date: Tue, 7 Apr 2026 19:57:12 +0200 Subject: [PATCH 04/11] Hopefully made the docs better :) --- src/CacheLibrary/CacheLibrary.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/CacheLibrary/CacheLibrary.py b/src/CacheLibrary/CacheLibrary.py index 97f9b03..536fca5 100644 --- a/src/CacheLibrary/CacheLibrary.py +++ b/src/CacheLibrary/CacheLibrary.py @@ -177,8 +177,8 @@ def cache_retrieve_value_from_collection( Retrieve the first value from a cached collection. - The value is automatically removed from the collection. Because of this, you will receive a - new value every time you use this keyword. + The value is automatically removed from the collection. This way you will receive a new + value every time you use this keyword. | ${user} = Cache Retrieve Value From Collection user-accounts @@ -196,15 +196,17 @@ def cache_retrieve_value_from_collection( == Pick pabot process ID == Always retrieve the same value within a pabot process. This ensures that a value is only - used once at the same moment in time. The value will be used multiple times, just not at + used once at the same moment in time. The value will be used multiple times, just never at the same time. - For example, this could be useful when testing an application where a user can only be - logged in once. If you log in with two browsers at the same time, the user is logged out in - the first browser. Using the `pabot` pick option here will ensure that: + For example, this could be useful in the following scenario: - - A user is only logged in once. It's never used in multiple tests at the same time. - - A user is used for multiple tests. + > In the application under test, a user can only have a logged in session in one browser. + If you log in from a second browser, the user is logged out in the first browser. The + collection contains multiple valid test users. + + Using the `pabot` pick option here will ensure that a user will never log in twice at the + same time. This works with and without pabot. When you run without pabot, it will pick the first value in the collection. From 6412c0c59aa39c3431957d1462d392cf0cf36ed8 Mon Sep 17 00:00:00 2001 From: Lakitna Date: Thu, 9 Apr 2026 18:50:43 +0200 Subject: [PATCH 05/11] Changed docstring, renamed pick strategy to 'parallel process' --- src/CacheLibrary/CacheLibrary.py | 55 +++++++++++++++++++------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/src/CacheLibrary/CacheLibrary.py b/src/CacheLibrary/CacheLibrary.py index 536fca5..9b876de 100644 --- a/src/CacheLibrary/CacheLibrary.py +++ b/src/CacheLibrary/CacheLibrary.py @@ -158,7 +158,7 @@ def cache_retrieve_value(self, key: CacheKey) -> CacheValue | None: def cache_retrieve_value_from_collection( self, key: CacheKey, - pick: Literal["first", "last", "random", "pabot"] = "first", + pick: Literal["first", "last", "random", "parallel process"] = "first", remove_value: bool = True, # noqa: FBT001, FBT002 ) -> CacheValue | None: """ @@ -167,9 +167,25 @@ def cache_retrieve_value_from_collection( Will return a single value from a collection stored in the cache, or `None` if there is no value. - | `key` | Name of the collection | - | `pick=first` | How to pick a value from the collection. Can be 'first', 'last', 'random', or 'pabot' | - | `remove_value=True` | Should the value be removed from the collection | + | `key` | Name of the collection | + | `pick=first` | How to pick a value from the collection. Can be 'first', 'last', 'random', or 'parallel process' | + | `remove_value=True` | Should the value be removed from the collection | + + = Pick strategies = + + - `first`: pick the first value + - `last`: pick the last value + - `random`: pick a random value + - `parallel process`: pick a value unique per parallel process. + + == Pick strategy `parallel process` == + + With `parallel process`, each parallel process gets its own fixed value, preventing multiple + processes from using the same value at the same time. Within a process, the same value is + returned on each call. + + Designed to be used with `remove_value=False` and Pabot. When not using Pabot, + `parallel process` behaves the same as `first`. = Examples = @@ -193,25 +209,17 @@ def cache_retrieve_value_from_collection( -------------------- - == Pick pabot process ID == - - Always retrieve the same value within a pabot process. This ensures that a value is only - used once at the same moment in time. The value will be used multiple times, just never at - the same time. - - For example, this could be useful in the following scenario: + == Pick parallel process value == - > In the application under test, a user can only have a logged in session in one browser. - If you log in from a second browser, the user is logged out in the first browser. The - collection contains multiple valid test users. + Ensure each parallel process uses a different user from a cached collection of users. - Using the `pabot` pick option here will ensure that a user will never log in twice at the - same time. + This could be useful in the following scenario: - This works with and without pabot. When you run without pabot, it will pick the first value - in the collection. + In the application under test, a user can only have a logged in session in one browser + at a time. If you log in from a second browser, the user is logged out in the first + browser. - | ${user} = Cache Retrieve Value From Collection user-accounts pick=pabot remove_value=${False} + | ${user} = Cache Retrieve Value From Collection user-accounts pick=parallel process remove_value=${False} """ # noqa: E501 cache = self.cache_file.get() cache = self._ensure_complete_cache(cache)["COLLECTION"] @@ -236,11 +244,12 @@ def cache_retrieve_value_from_collection( index = -1 elif pick == "random": index = random.randint(0, len(values) - 1) # noqa: S311 - elif pick == "pabot": - index = self._pick_strategy_pabot() + elif pick == "parallelprocess": + index = self._pick_strategy_parallel_process() else: msg = ( - f"Unexpected pick '{pick}'. Expected one of 'first', 'last', 'random', or 'pabot'." + f"Unexpected pick '{pick}'. " + "Expected one of 'first', 'last', 'random', or 'parallel process'." ) raise ValueError(msg) @@ -250,7 +259,7 @@ def cache_retrieve_value_from_collection( return value - def _pick_strategy_pabot(self) -> int: + def _pick_strategy_parallel_process(self) -> int: index = BuiltIn().get_variable_value("${PABOTEXECUTIONPOOLID}", 0) try: index = int(cast(str | int, index)) From af593c16ec14816d47a2f60f2f00e6816bba73cf Mon Sep 17 00:00:00 2001 From: Lakitna Date: Thu, 9 Apr 2026 18:54:17 +0200 Subject: [PATCH 06/11] Changed some docstring whitespace --- src/CacheLibrary/CacheLibrary.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/CacheLibrary/CacheLibrary.py b/src/CacheLibrary/CacheLibrary.py index 9b876de..79bded2 100644 --- a/src/CacheLibrary/CacheLibrary.py +++ b/src/CacheLibrary/CacheLibrary.py @@ -211,13 +211,11 @@ def cache_retrieve_value_from_collection( == Pick parallel process value == - Ensure each parallel process uses a different user from a cached collection of users. + Ensure each parallel process uses a different user from a cached collection of users. This + could be useful in the following scenario: - This could be useful in the following scenario: - - In the application under test, a user can only have a logged in session in one browser - at a time. If you log in from a second browser, the user is logged out in the first - browser. + In the application under test, a user can only have a logged in session in one browser at a + time. If you log in from a second browser, the user is logged out in the first browser. | ${user} = Cache Retrieve Value From Collection user-accounts pick=parallel process remove_value=${False} """ # noqa: E501 From 4aa90d5f8555c1ff6da31aee7ee0b94c43b0e33d Mon Sep 17 00:00:00 2001 From: Lakitna Date: Thu, 9 Apr 2026 19:07:54 +0200 Subject: [PATCH 07/11] Fixed tests --- src/CacheLibrary/CacheLibrary.py | 2 +- test/integration/run-multi-value.robot | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/CacheLibrary/CacheLibrary.py b/src/CacheLibrary/CacheLibrary.py index 79bded2..15c641a 100644 --- a/src/CacheLibrary/CacheLibrary.py +++ b/src/CacheLibrary/CacheLibrary.py @@ -242,7 +242,7 @@ def cache_retrieve_value_from_collection( index = -1 elif pick == "random": index = random.randint(0, len(values) - 1) # noqa: S311 - elif pick == "parallelprocess": + elif pick == "parallel process": index = self._pick_strategy_parallel_process() else: msg = ( diff --git a/test/integration/run-multi-value.robot b/test/integration/run-multi-value.robot index 727eaa3..03e319c 100644 --- a/test/integration/run-multi-value.robot +++ b/test/integration/run-multi-value.robot @@ -335,6 +335,9 @@ Test Template Pabot Pick Strategy END FOR ${_} IN RANGE ${5} - ${retrieved} = Cache Retrieve Value From Collection fixed-strings pick=pabot remove_value=False + ${retrieved} = Cache Retrieve Value From Collection + ... fixed-strings + ... pick=parallel process + ... remove_value=False Should Be Equal ${retrieved} ${expected_value} END From ae2182eb6548c39ac5df5220eeb60d558be1afcb Mon Sep 17 00:00:00 2001 From: Lakitna Date: Thu, 9 Apr 2026 19:08:13 +0200 Subject: [PATCH 08/11] Stabilized timing-dependent tests --- pyproject.toml | 1 + tasks.py | 6 +++--- test/integration/run.robot | 6 +++++- uv.lock | 14 ++++++++++++++ 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5132442..030c242 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,7 @@ dev = [ "invoke (>=2.2.1)", # Required for faker <6.0.0 "setuptools (>=80.0.0,<81.0.0)", + "robotframework-retryfailed>=0.2.0", ] [tool.uv.build-backend] diff --git a/tasks.py b/tasks.py index e733fb6..6fb94ad 100644 --- a/tasks.py +++ b/tasks.py @@ -98,19 +98,19 @@ def test_integration(c: Context): @task def test_integration_sync(c: Context): print("Integration tests: Synchronous") - c.run("uv run robot test/integration") + c.run("uv run robot --listener RetryFailed test/integration") @task def test_integration_parallel_suite_level(c: Context): print("Integration tests: Parallel, Suite level split") - c.run("uv run pabot --pabotlib test/integration") + c.run("uv run pabot --pabotlib --listener RetryFailed test/integration") @task def test_integration_parallel_test_level(c: Context): print("Integration tests: Parallel, Test level split") - c.run("uv run pabot --testlevelsplit --pabotlib test/integration") + c.run("uv run pabot --testlevelsplit --pabotlib --listener RetryFailed test/integration") @task diff --git a/test/integration/run.robot b/test/integration/run.robot index b8dabe8..2ad6975 100644 --- a/test/integration/run.robot +++ b/test/integration/run.robot @@ -122,7 +122,11 @@ Remove stored data Should Be Equal ${postRemove} ${None} Store with expiration time - Cache Store Value random-data amazing data expire_in_seconds=1 + [Documentation] + ... Test relies on timing within the test. This timing can be messed up by locks aquired by + ... other tests. Retry stabilizes the run. + [Tags] test:retry(2) + Cache Store Value random-data amazing data expire_in_seconds=2 ${preSleep} = Cache Retrieve Value random-data Sleep 2s ${postSleep} = Cache Retrieve Value random-data diff --git a/uv.lock b/uv.lock index 81e9a33..519fbed 100644 --- a/uv.lock +++ b/uv.lock @@ -313,6 +313,7 @@ dependencies = [ dev = [ { name = "invoke" }, { name = "robotframework-faker" }, + { name = "robotframework-retryfailed" }, { name = "robotframework-robocop" }, { name = "ruff" }, { name = "setuptools" }, @@ -329,6 +330,7 @@ requires-dist = [ dev = [ { name = "invoke", specifier = ">=2.2.1" }, { name = "robotframework-faker", specifier = ">=5.0.0" }, + { name = "robotframework-retryfailed", specifier = ">=0.2.0" }, { name = "robotframework-robocop", specifier = ">=7.0.0,<8.0.0" }, { name = "ruff", specifier = ">=0.9.1,<0.10.0" }, { name = "setuptools", specifier = ">=80.0.0,<81.0.0" }, @@ -360,6 +362,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f5/15/f5da87828441f53f5126dda3dd1fdf1fd73b8f5fdf490659b56dd2dc3a80/robotframework_pabot-5.2.2-py3-none-any.whl", hash = "sha256:e7b95d031e9f04b77312b04a1fd835b9791f022b3436b63b507a720bfec05bb4", size = 71801, upload-time = "2026-02-18T21:04:21.313Z" }, ] +[[package]] +name = "robotframework-retryfailed" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "robotframework" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4b/99/4ffc2253cbff664c93f4fe63663a0d0a68862c7bbe40aea6f324fd371ef3/robotframework-retryfailed-0.2.0.tar.gz", hash = "sha256:c134a924f480e2666916bcb019a7e255d7229bc51a3747e849bd1e7931ed6eb3", size = 7611, upload-time = "2022-10-09T20:13:48.33Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/80/144bca38aa894ec876f7943d3fd62fa57d9be937cc0be9e5d7e3df9837e3/robotframework_retryfailed-0.2.0-py3-none-any.whl", hash = "sha256:b389dadb446a4d7356d7e953aabfdf2b0497d51df11d3da80abe9d4a8a0992c3", size = 8638, upload-time = "2022-10-09T20:13:46.396Z" }, +] + [[package]] name = "robotframework-robocop" version = "7.2.0" From fbc1cbad846a833e15e11d83adc72381d40c1d15 Mon Sep 17 00:00:00 2001 From: Lakitna Date: Thu, 9 Apr 2026 19:11:28 +0200 Subject: [PATCH 09/11] Moved to actions/checkout@6 --- .github/workflows/ci.yaml | 2 +- .github/workflows/docs.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 929ce73..3ed6745 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -23,7 +23,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 39bd7c4..7630ee7 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -29,7 +29,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Pages uses: actions/configure-pages@v5 From e6f38e0761e1f932741172fcff0840663a594eb8 Mon Sep 17 00:00:00 2001 From: Lakitna Date: Thu, 9 Apr 2026 19:27:03 +0200 Subject: [PATCH 10/11] Integraiton tests are now randomized --- tasks.py | 11 ++++++++--- test/integration/run.robot | 3 ++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/tasks.py b/tasks.py index 6fb94ad..98c040e 100644 --- a/tasks.py +++ b/tasks.py @@ -98,19 +98,24 @@ def test_integration(c: Context): @task def test_integration_sync(c: Context): print("Integration tests: Synchronous") - c.run("uv run robot --listener RetryFailed test/integration") + c.run("uv run robot --listener RetryFailed --randomize all test/integration") @task def test_integration_parallel_suite_level(c: Context): print("Integration tests: Parallel, Suite level split") - c.run("uv run pabot --pabotlib --listener RetryFailed test/integration") + c.run("uv run pabot --pabotlib --listener RetryFailed --randomize all test/integration") @task def test_integration_parallel_test_level(c: Context): print("Integration tests: Parallel, Test level split") - c.run("uv run pabot --testlevelsplit --pabotlib --listener RetryFailed test/integration") + c.run( + "uv run pabot " + "--testlevelsplit --pabotlib " + "--listener RetryFailed --randomize all " + "test/integration", + ) @task diff --git a/test/integration/run.robot b/test/integration/run.robot index 2ad6975..0f2165b 100644 --- a/test/integration/run.robot +++ b/test/integration/run.robot @@ -125,7 +125,8 @@ Store with expiration time [Documentation] ... Test relies on timing within the test. This timing can be messed up by locks aquired by ... other tests. Retry stabilizes the run. - [Tags] test:retry(2) + ... The retry is _not_ hiding a race condition in this situation. + [Tags] test:retry(1) Cache Store Value random-data amazing data expire_in_seconds=2 ${preSleep} = Cache Retrieve Value random-data Sleep 2s From ba16ef65a1358e80843520fac0fc15193273a46d Mon Sep 17 00:00:00 2001 From: Lakitna Date: Thu, 9 Apr 2026 19:45:11 +0200 Subject: [PATCH 11/11] Removed test randomization. It does not work with old pabot versions --- tasks.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tasks.py b/tasks.py index 98c040e..29d90f5 100644 --- a/tasks.py +++ b/tasks.py @@ -98,23 +98,20 @@ def test_integration(c: Context): @task def test_integration_sync(c: Context): print("Integration tests: Synchronous") - c.run("uv run robot --listener RetryFailed --randomize all test/integration") + c.run("uv run robot --listener RetryFailed test/integration") @task def test_integration_parallel_suite_level(c: Context): print("Integration tests: Parallel, Suite level split") - c.run("uv run pabot --pabotlib --listener RetryFailed --randomize all test/integration") + c.run("uv run pabot --pabotlib --listener RetryFailed test/integration") @task def test_integration_parallel_test_level(c: Context): print("Integration tests: Parallel, Test level split") c.run( - "uv run pabot " - "--testlevelsplit --pabotlib " - "--listener RetryFailed --randomize all " - "test/integration", + "uv run pabot --testlevelsplit --pabotlib --listener RetryFailed test/integration", )