Repository: robotframework/robotframework

Issue:
Enhance result package to expose generated attribute from output.xml
The `output.xml` has  `<robot generated="year-month-dayTHH:MM:SS.milliseconds" …>` in the xml. It would be handy to expose the `generated` attribute from the RF result package. We have a need to  use the `generated` attribute when reading test result from multiple runs in CI and plotting the results in time series manner.  

Discussion:

Patch:
diff --git a/src/robot/result/executionresult.py b/src/robot/result/executionresult.py
index b2e77eb82..168efa9b2 100644
--- a/src/robot/result/executionresult.py
+++ b/src/robot/result/executionresult.py
@@ -64,6 +64,7 @@ class Result:
         #: :class:`~.executionerrors.ExecutionErrors` object.
         self.errors = errors or ExecutionErrors()
         self.generated_by_robot = True
+        self.generation_time = None
         self._status_rc = True
         self._stat_config = {}
         self.rpa = rpa
diff --git a/src/robot/result/xmlelementhandlers.py b/src/robot/result/xmlelementhandlers.py
index 3c657c06d..31e57aea5 100644
--- a/src/robot/result/xmlelementhandlers.py
+++ b/src/robot/result/xmlelementhandlers.py
@@ -60,6 +60,9 @@ class ElementHandler:
 
     def _legacy_timestamp(self, elem, attr_name):
         ts = elem.get(attr_name)
+        return self._parse_legacy_timestamp(ts)
+
+    def _parse_legacy_timestamp(self, ts):
         if ts == 'N/A' or not ts:
             return None
         ts = ts.ljust(24, '0')
@@ -85,10 +88,19 @@ class RobotHandler(ElementHandler):
     def start(self, elem, result):
         generator = elem.get('generator', 'unknown').split()[0].upper()
         result.generated_by_robot = generator == 'ROBOT'
+        result.generation_time = self._parse_generation_time(elem.get('generated'))
         if result.rpa is None:
             result.rpa = elem.get('rpa', 'false') == 'true'
         return result
 
+    def _parse_generation_time(self, generated):
+        if not generated:
+            return None
+        try:
+            return datetime.fromisoformat(generated)
+        except ValueError:
+            return self._parse_legacy_timestamp(generated)
+
 
 @ElementHandler.register
 class SuiteHandler(ElementHandler):


Test patch:
diff --git a/utest/result/test_resultbuilder.py b/utest/result/test_resultbuilder.py
index e55a7edfe..f30870a64 100644
--- a/utest/result/test_resultbuilder.py
+++ b/utest/result/test_resultbuilder.py
@@ -24,6 +24,13 @@ class TestBuildingSuiteExecutionResult(unittest.TestCase):
         self.suite = self.result.suite
         self.test = self.suite.tests[0]
 
+    def test_result_has_generation_time(self):
+        assert_equal(self.result.generation_time, datetime(2023, 9, 8, 12, 1, 47, 906104))
+        result = ExecutionResult("<robot><suite/></robot>")
+        assert_equal(result.generation_time, None)
+        result = ExecutionResult("<robot generated='20111024 13:41:20.873'><suite/></robot>")
+        assert_equal(result.generation_time, datetime(2011, 10, 24, 13, 41, 20, 873000))
+
     def test_suite_is_built(self):
         assert_equal(self.suite.source, Path('normal.html'))
         assert_equal(self.suite.name, 'Normal')