Skip to content

Commit a5eb126

Browse files
Add ${EXECUTION ID} automatic variable and --executionid option (#2743)
Generate a unique ID for each execution and make it available as the `${EXECUTION ID}` automatic variable. The ID is also stored as `Execution ID` metadata of the top level suite so that it is visible in logs and reports and saved in the output file. By default the ID is a random UUID. It can be set explicitly with the new `--executionid` option, for example to share a common ID across parallel executions or to use an application specific scheme. This implements the core of the design outlined in the issue. Exposing the existing test and suite IDs as automatic variables, also proposed there, is left out of scope and can be handled separately.
1 parent 8bdd141 commit a5eb126

7 files changed

Lines changed: 58 additions & 0 deletions

File tree

‎atest/robot/variables/builtin_variables.robot‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,6 @@ $CURDIR
5353

5454
\${LOG LEVEL}
5555
Check Test Case ${TESTNAME}
56+
57+
\${EXECUTION ID}
58+
Check Test Case ${TESTNAME}

‎atest/testdata/variables/builtin_variables.robot‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,8 @@ $CURDIR
126126
Set Log Level NONE
127127
Should Be Equal ${LOG_LEVEL} NONE
128128
[Teardown] Set Log Level INFO
129+
130+
\${EXECUTION ID}
131+
Should Not Be Empty ${EXECUTION ID}
132+
Should Match Regexp ${EXECUTION ID} (?i)^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$
133+
Should Be Equal ${EXECUTION ID} ${SUITE METADATA}[Execution ID]

‎doc/userguide/src/CreatingTestData/Variables.rst‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1651,6 +1651,11 @@ can be changed dynamically using keywords from the `BuiltIn`_ library.
16511651
| ${DEBUG FILE} | An absolute path to the `debug file`_ as a string or | Everywhere |
16521652
| | a string `NONE` if the debug file is not created. | |
16531653
+------------------------+-------------------------------------------------------+------------+
1654+
| ${EXECUTION ID} | Unique ID for the current execution. Same value as | Everywhere |
1655+
| | the ``Execution ID`` suite metadata. Given with the | |
1656+
| | :option:`--executionid` option or, by default, a | |
1657+
| | generated UUID. New in RF 7.5. | |
1658+
+------------------------+-------------------------------------------------------+------------+
16541659
| &{OPTIONS} | A dictionary exposing command line options. The | Everywhere |
16551660
| | dictionary keys match the command line options and | |
16561661
| | can be accessed both like `${OPTIONS}[key]` and | |

‎src/robot/conf/settings.py‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import warnings
2222
from datetime import datetime
2323
from pathlib import Path
24+
from uuid import uuid4
2425

2526
from robot.errors import DataError, FrameworkError
2627
from robot.output import LOGGER, LogLevel
@@ -502,8 +503,10 @@ class RobotSettings(_BaseSettings):
502503
"ConsoleMarkers" : ("consolemarkers", "AUTO"),
503504
"DebugFile" : ("debugfile", None),
504505
"Language" : ("language", []),
506+
"ExecutionId" : ("executionid", None),
505507
} # fmt: skip
506508
_languages = None
509+
_execution_id = None
507510

508511
def get_rebot_settings(self):
509512
settings = RebotSettings()
@@ -553,6 +556,18 @@ def languages(self):
553556
self._raise_invalid("Language", err)
554557
return self._languages
555558

559+
@property
560+
def execution_id(self):
561+
"""Unique ID for the current execution.
562+
563+
Given with the ``--executionid`` option or, by default, a generated
564+
UUID. The value is generated only once and cached so that it stays the
565+
same throughout the execution.
566+
"""
567+
if self._execution_id is None:
568+
self._execution_id = self["ExecutionId"] or str(uuid4())
569+
return self._execution_id
570+
556571
@property
557572
def suite_config(self):
558573
return {

‎src/robot/run.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,12 @@
118118
-M --metadata name:value * Set metadata of the top level suite. Value can
119119
contain formatting and be read from a file similarly
120120
as --doc. Example: --metadata Version:1.2
121+
--executionid id Unique ID for the current execution. By default a
122+
random UUID is generated. The ID is available during
123+
execution as the automatic variable ${EXECUTION ID}
124+
and is set as metadata `Execution ID` of the top level
125+
suite so that it is visible in logs and reports and
126+
stored in the output file.
121127
-G --settag tag * Sets given tag(s) to all executed tests.
122128
-t --test name * Select tests by name or by long name containing also
123129
parent suite name like `Parent.Test`. Name is case
@@ -485,6 +491,11 @@ def main(self, datasources, **options):
485491
)
486492
suite.visit(modifier)
487493
suite.configure(**settings.suite_config)
494+
# Expose the execution ID as top level suite metadata so that it is
495+
# visible in logs and reports and stored in the output file. An explicit
496+
# `--metadata` with the same name (set above) takes precedence.
497+
if "Execution ID" not in suite.metadata:
498+
suite.metadata["Execution ID"] = settings.execution_id
488499
settings.rpa = suite.validate_execution_mode()
489500
with pyloggingconf.robot_handler_enabled(settings.log_level):
490501
old_max_error_lines = text.MAX_ERROR_LINES

‎src/robot/variables/scopes.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ def _set_built_in_variables(self, settings):
246246
("${LOG_FILE}", str(settings.log or "NONE")),
247247
("${DEBUG_FILE}", str(settings.debug_file or "NONE")),
248248
("${LOG_LEVEL}", settings.log_level),
249+
("${EXECUTION_ID}", settings.execution_id),
249250
("${PREV_TEST_NAME}", ""),
250251
("${PREV_TEST_STATUS}", ""),
251252
("${PREV_TEST_MESSAGE}", ""),

‎utest/conf/test_settings.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import unittest
33
from os.path import abspath, dirname, join, normpath
44
from pathlib import Path
5+
from uuid import UUID
56

67
from robot.conf.settings import _BaseSettings, RebotSettings, RobotSettings
78
from robot.errors import DataError
@@ -213,5 +214,22 @@ def _verify_invalid_log_level(self, input):
213214
self.assertRaises(DataError, RobotSettings, {"loglevel": input})
214215

215216

217+
class TestExecutionId(unittest.TestCase):
218+
219+
def test_default_is_generated_uuid(self):
220+
# UUID(...) raises ValueError if the value is not a valid UUID.
221+
UUID(RobotSettings().execution_id)
222+
223+
def test_can_be_given_from_command_line(self):
224+
assert_equal(RobotSettings(executionid="my-id").execution_id, "my-id")
225+
226+
def test_is_cached(self):
227+
settings = RobotSettings()
228+
assert_equal(settings.execution_id, settings.execution_id)
229+
230+
def test_generated_ids_are_unique(self):
231+
assert_true(RobotSettings().execution_id != RobotSettings().execution_id)
232+
233+
216234
if __name__ == "__main__":
217235
unittest.main()

0 commit comments

Comments
 (0)