holado 0.9.0__py3-none-any.whl → 0.9.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of holado might be problematic. Click here for more details.

Files changed (40) hide show
  1. holado/common/context/session_context.py +6 -5
  2. {holado-0.9.0.dist-info → holado-0.9.1.dist-info}/METADATA +1 -1
  3. {holado-0.9.0.dist-info → holado-0.9.1.dist-info}/RECORD +39 -39
  4. holado_core/common/block/base.py +3 -3
  5. holado_core/common/tools/path_manager.py +1 -1
  6. holado_django/server/django_projects/rest_api/rest_api/urls.py +1 -1
  7. holado_docker/tests/behave/steps/tools/docker_controller/client_steps.py +2 -2
  8. holado_docker/tools/docker_controller/client/rest/docker_controller_client.py +38 -1
  9. holado_docker/tools/docker_controller/server/run_docker_controller_in_docker.sh +3 -3
  10. holado_examples/tests/behave/testing_solution/environment.py +3 -3
  11. holado_examples/tests/behave/testing_solution/src/context/session_context.py +2 -2
  12. holado_examples/tests/behave/testing_solution/steps/config_steps.py +1 -1
  13. holado_multitask/multithreading/loopthread.py +4 -4
  14. holado_python/common/tools/datetime.py +35 -17
  15. holado_python/standard_library/ssl/resources/certificates/tcpbin.crt +16 -16
  16. holado_python/standard_library/ssl/resources/certificates/tcpbin.key +26 -26
  17. holado_python/tests/behave/steps/standard_library/datetime_steps.py +2 -2
  18. holado_rabbitmq/tools/rabbitmq/rabbitmq_manager.py +3 -3
  19. holado_report/campaign/campaign_manager.py +8 -3
  20. holado_report/report/builders/detailed_scenario_failed_report_builder.py +11 -7
  21. holado_report/report/builders/failure_report_builder.py +4 -3
  22. holado_report/report/builders/short_scenario_failed_report_builder.py +6 -5
  23. holado_report/report/builders/summary_report_builder.py +1 -1
  24. holado_report/report/builders/summary_scenario_failed_report_builder.py +6 -5
  25. holado_report/report/builders/summary_scenario_report_builder.py +12 -11
  26. holado_report/report/execution_historic.py +4 -2
  27. holado_report/report/report_manager.py +63 -13
  28. holado_rest/api/rest/rest_client.py +5 -1
  29. holado_test/__init__.py +3 -0
  30. holado_test/common/context/feature_context.py +3 -3
  31. holado_test/common/context/scenario_context.py +3 -3
  32. holado_test/common/context/step_context.py +3 -3
  33. holado_test/test_server/client/rest/test_server_client.py +117 -0
  34. holado_test/test_server/server/run_test_server_in_docker.sh +7 -7
  35. holado_value/common/tools/unique_value_manager.py +2 -1
  36. test_holado/environment.py +1 -1
  37. test_holado/logging.conf +1 -0
  38. holado_docker/tools/docker_controller/docker_controller_manager.py +0 -46
  39. {holado-0.9.0.dist-info → holado-0.9.1.dist-info}/WHEEL +0 -0
  40. {holado-0.9.0.dist-info → holado-0.9.1.dist-info}/licenses/LICENSE +0 -0
@@ -22,7 +22,7 @@ import os
22
22
  from holado_core.common.tools.converters.converter import Converter
23
23
  from enum import Enum
24
24
  from holado_python.common.enums import ArithmeticOperator
25
- from datetime import timedelta, timezone
25
+ from datetime import timedelta
26
26
  from dateutil.relativedelta import relativedelta
27
27
  import math
28
28
  from decimal import Decimal
@@ -39,6 +39,9 @@ FORMAT_DATETIME_ISO = '%Y-%m-%dT%H:%M:%S.%fZ'
39
39
  FORMAT_DATETIME_ISO_SECONDS = '%Y-%m-%dT%H:%M:%SZ'
40
40
  FORMAT_DATETIME_HUMAN_SECOND = '%Y-%m-%d %H:%M:%S'
41
41
 
42
+ TIMEZONE_LOCAL = datetime.datetime.now().astimezone().tzinfo
43
+ TIMEZONE_UTC = datetime.timezone.utc
44
+
42
45
 
43
46
  class DurationUnit(str, Enum):
44
47
  # Nanosecond = "nanosecond"
@@ -55,11 +58,16 @@ class DurationUnit(str, Enum):
55
58
 
56
59
 
57
60
  class DateTime(object):
61
+ """ Tools to manipulate datetimes.
62
+ Note: Naive and aware datetimes cannot be compared. Thus HolAdo methods always set datetime timezone when it is known.
63
+ In methods unable to define the timezone (ex: str_2_datetime), it is highly recommended to specify 'tz' argument.
64
+ """
58
65
 
59
66
  __is_system_time_in_tai = None
60
67
  __is_system_time_expected_in_tai = None
61
68
  __utc_to_tai_timedelta = None
62
69
 
70
+
63
71
  def __init__(self):
64
72
  pass
65
73
 
@@ -71,22 +79,22 @@ class DateTime(object):
71
79
  return cls.__is_system_time_in_tai
72
80
 
73
81
  @classmethod
74
- def now(cls):
75
- return datetime.datetime.now()
82
+ def now(cls, tz=TIMEZONE_LOCAL):
83
+ return datetime.datetime.now(tz=tz)
76
84
 
77
85
  @classmethod
78
86
  def utcnow(cls):
79
87
  if cls.is_system_time_in_tai():
80
- return datetime.datetime.utcnow() - cls.utc_to_tai_timedelta()
88
+ return cls.now(TIMEZONE_UTC) - cls.utc_to_tai_timedelta()
81
89
  else:
82
- return datetime.datetime.utcnow()
90
+ return cls.now(TIMEZONE_UTC)
83
91
 
84
92
  @classmethod
85
93
  def tainow(cls):
86
94
  if cls.is_system_time_in_tai():
87
- return datetime.datetime.utcnow()
95
+ return cls.now(TIMEZONE_UTC)
88
96
  else:
89
- return datetime.datetime.utcnow() + cls.utc_to_tai_timedelta()
97
+ return cls.now(TIMEZONE_UTC) + cls.utc_to_tai_timedelta()
90
98
 
91
99
  @classmethod
92
100
  def utc_to_tai_timedelta(cls):
@@ -126,8 +134,15 @@ class DateTime(object):
126
134
  return res
127
135
 
128
136
  @classmethod
129
- def str_2_datetime(cls, dt_str, dt_format=None, default_timezone=timezone.utc):
130
- """Convert string to datetime instance"""
137
+ def str_2_datetime(cls, dt_str, dt_format=None, tz=None):
138
+ """Convert string to datetime instance
139
+ @param dt_format If defined, use this format to parse string rather than using first matching format
140
+ @param tz If defined, return a datetime with this timezone.
141
+ If string already defines a timezone, the datetime is converted to this timezone,
142
+ else the string is supposed to represent a datetime in this timezone and timezone is set in result datetime.
143
+ Note: Naive and aware datetimes cannot be compared. Thus HolAdo methods always set datetime timezone when it is known.
144
+ It is highly recommended to specify 'tz' argument
145
+ """
131
146
  res = None
132
147
  if dt_format is None:
133
148
  res = dateutil.parser.parse(dt_str)
@@ -135,8 +150,11 @@ class DateTime(object):
135
150
  res = datetime.datetime.strptime(dt_str, dt_format)
136
151
 
137
152
  # Set timezone if needed
138
- if res.tzinfo is None and default_timezone is not None:
139
- res = res.replace(tzinfo=default_timezone)
153
+ if tz is not None:
154
+ if res.tzinfo is None:
155
+ res = res.replace(tzinfo=tz)
156
+ else:
157
+ res = res.astimezone(tz)
140
158
 
141
159
  return res
142
160
 
@@ -162,19 +180,19 @@ class DateTime(object):
162
180
  return res
163
181
 
164
182
  @classmethod
165
- def _get_datetime(cls, dt):
183
+ def _get_datetime(cls, dt, tz_in_str=None):
166
184
  res = None
167
185
  if dt is not None:
168
186
  if isinstance(dt, datetime.datetime):
169
187
  res = dt
170
188
  elif isinstance(dt, str):
171
- res = DateTime.str_2_datetime(dt)
189
+ res = DateTime.str_2_datetime(dt, tz=tz_in_str)
172
190
  else:
173
191
  raise TechnicalException(f"Datetime can be a datetime, a str containing a datetime, or None")
174
192
  return res
175
193
 
176
194
  @classmethod
177
- def _get_epoch_datetime(cls, epoch):
195
+ def _get_epoch_datetime(cls, epoch, tz_in_str=None):
178
196
  res = None
179
197
  if epoch is not None:
180
198
  if isinstance(epoch, datetime.datetime):
@@ -183,7 +201,7 @@ class DateTime(object):
183
201
  if epoch in ['EPOCH_1970', 'EPOCH_JULIAN_CNES', 'EPOCH_JULIAN_NASA']:
184
202
  res = eval(epoch)
185
203
  else:
186
- res = DateTime.str_2_datetime(epoch)
204
+ res = DateTime.str_2_datetime(epoch, tz=tz_in_str)
187
205
  else:
188
206
  raise TechnicalException(f"Epoch can be a datetime, a str containing a datetime, a str containing an EPOCH name, or None")
189
207
  return res
@@ -296,7 +314,7 @@ class DateTime(object):
296
314
  """
297
315
  dt_src = dt
298
316
  if isinstance(dt_src, str):
299
- dt_src = cls.str_2_datetime(dt_src, dt_format)
317
+ dt_src = cls.str_2_datetime(dt_src, dt_format, tz=TIMEZONE_UTC)
300
318
 
301
319
  return dt_src + DateTime.utc_to_tai_timedelta()
302
320
 
@@ -308,7 +326,7 @@ class DateTime(object):
308
326
  """
309
327
  dt_src = dt
310
328
  if isinstance(dt_src, str):
311
- dt_src = cls.str_2_datetime(dt_src, dt_format)
329
+ dt_src = cls.str_2_datetime(dt_src, dt_format, tz=TIMEZONE_UTC)
312
330
 
313
331
  return dt_src - DateTime.utc_to_tai_timedelta()
314
332
 
@@ -2,20 +2,20 @@
2
2
  MIIDZTCCAk2gAwIBAgIBKjANBgkqhkiG9w0BAQsFADCBizELMAkGA1UEBhMCVVMx
3
3
  CzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQ8wDQYDVQQKDAZ0
4
4
  Y3BiaW4xDDAKBgNVBAsMA29wczETMBEGA1UEAwwKdGNwYmluLmNvbTEjMCEGCSqG
5
- SIb3DQEJARYUaGFycnliYWdkaUBnbWFpbC5jb20wHhcNMjUwOTI0MTAzMzE5WhcN
6
- MjUwOTI1MTAzMzE5WjAcMRowGAYDVQQDDBF0Y3BiaW4uY29tLWNsaWVudDCCASIw
7
- DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALuFFkk1M7uFHl+/XHsVOYe3BAjO
8
- FIXrlDFk0D3G9QAwjpuDnCPYSrhAHpSxdVrp+5RV9L/OFj7ome2O9thFKvQJaG89
9
- g44Jpa1SnVysonZui23GJZUcbS2td8tLXVyE6e+v8CeIjeQvjKqkS/t4t+4SkBCU
10
- 4PUt6aP+PuK1aG0/7KqBoctC12c0RwRqh+eJAHBxlb69/OzdOTTpXpXMyzWuyLTp
11
- gzHbBhbtMHle/0dk/gHmh+/dAASCnoHfIOHOvqRW57p/SpnKW80QkshxArS6/mvm
12
- 7RkxYpQEjrAWpk24CcR+LYbL5Q89oXvxmXg4V2IiEvy4oXRh9vXG/obe06UCAwEA
13
- AaNCMEAwHQYDVR0OBBYEFD7PxtLlvXy3LXcd2TsiB6KewQxeMB8GA1UdIwQYMBaA
14
- FOLuMowBSAZfV5v82LmlaIIOvU/DMA0GCSqGSIb3DQEBCwUAA4IBAQAwUvYMFteT
15
- N/CVVickwg/MpAv2/HLr5NeajfKQO9ya7K9ymHF8WU2S9MSwXUuJ4/wGzNwbpXmj
16
- hsbh0W06ZL1ClrWr/US6lkHZegbVBGkF3qwDP0kMYVXEp6LcEy0e6lX4ey9t342H
17
- RBxbxmzXsp1plV4PecWXGt7LF18kzMe57sbGmSgDZ0LDGANFM97C72ytNrR2v5XB
18
- Mw1t5C5I9IowMWh484Ue/VUtGCekCvtrfO0ur1tta7y0mRIJu49MmPJeAdScefSj
19
- ke+Pf96v53zieSpMoV8mvI8f6Mjt3GMVq07VX7+IWufxJ7FXx7TQ1q3LBdOVtsZ7
20
- +biJwocFLiem
5
+ SIb3DQEJARYUaGFycnliYWdkaUBnbWFpbC5jb20wHhcNMjUxMDAyMTMzNjQ1WhcN
6
+ MjUxMDAzMTMzNjQ1WjAcMRowGAYDVQQDDBF0Y3BiaW4uY29tLWNsaWVudDCCASIw
7
+ DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK2xah/MsJKGnIpj84nYfBRCeAh8
8
+ MmN5dZwUCteehkX+wFhioEx1loAIqcqs6MPe7qQdeROuOZlGeiDR/6X0mnDsgyR4
9
+ qUnrIaRHS3EFWGtGp0WRLsQhKNvtxAHL7dErz7BoxTO5gV/w6dcT3uPORfZxkZdm
10
+ 6t01t7DwPFktopQju9E8eeMBKVKaO1RrclZKkWEyfuHP3wNpIE6GwsH8jCn4I+r/
11
+ Y8Pgd+k4EyCaH+ngtFbS0lEhxxuqTpPRYyQrlEDMa1i/nETuMiL2WFkjEO2gEX/p
12
+ w05GlO3YUTDpaXWUWZf+Ck7MYevrFDC57oSZvMwTn5pJrQztvNJjFc+YF6kCAwEA
13
+ AaNCMEAwHQYDVR0OBBYEFLuHuKWOQ/U1+OpiGgffNV1faI9qMB8GA1UdIwQYMBaA
14
+ FOLuMowBSAZfV5v82LmlaIIOvU/DMA0GCSqGSIb3DQEBCwUAA4IBAQBYIRlg6Paz
15
+ ALTKoBm8nRB19n4d7Ay+U1bUBCP2U5vZkUTSu05ttQXT72TaFIaT04pFs9uIHGFZ
16
+ 9yhP2DgbuCaexQrvf9yf6nnoCqLjlhJ8Y0NmscK14Hp9GgVSnkXeweL82gFsXB45
17
+ FaqqyDn20QiH14nwU2H7g8y/Ys0PIemhjG8UT7OjSLyhkaCkxwYy88N/2YjX3hGH
18
+ 9eTHvfMZ+9E7Urh3DevivhfPQnX/6lUAOK7Sr+WTIEjpZ89Lz/rLltRCXoXv75C4
19
+ PdRoxB2/lo+3+X1i4B7ujaduZDLaMyPcckbpuTYxjPZtoxhLyYkKgzQTUMwYsVrW
20
+ DoI6HuqlfzoH
21
21
  -----END CERTIFICATE-----
@@ -1,28 +1,28 @@
1
1
  -----BEGIN PRIVATE KEY-----
2
- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7hRZJNTO7hR5f
3
- v1x7FTmHtwQIzhSF65QxZNA9xvUAMI6bg5wj2Eq4QB6UsXVa6fuUVfS/zhY+6Jnt
4
- jvbYRSr0CWhvPYOOCaWtUp1crKJ2bottxiWVHG0trXfLS11chOnvr/AniI3kL4yq
5
- pEv7eLfuEpAQlOD1Lemj/j7itWhtP+yqgaHLQtdnNEcEaofniQBwcZW+vfzs3Tk0
6
- 6V6VzMs1rsi06YMx2wYW7TB5Xv9HZP4B5ofv3QAEgp6B3yDhzr6kVue6f0qZylvN
7
- EJLIcQK0uv5r5u0ZMWKUBI6wFqZNuAnEfi2Gy+UPPaF78Zl4OFdiIhL8uKF0Yfb1
8
- xv6G3tOlAgMBAAECggEBAJfv3L/ScKk20gaIebls2CFjo8wyliRwXbGr2qLDiw3t
9
- Sg8tkxzkCp7BrUWTh9/zCBoHj3i91f6A3v/Bv7Y6TPRQjyLa97XlkS0fbEuDwVsu
10
- dKCsVRU9ZCUHkbHRzcLCGv5PUsnldfp2oIY++yW1iWpH9hzJVIC0A8QRpAhzLjqd
11
- ls34VvUqo/BCdrYXVOLrQvqprak5R3UQFQ1JfAg8uh9BgTxs5W+EZJg050O4Z5Jt
12
- AAGZSNpyBWHbDopUFXFOfSuBSJx79qsN2u3uH8MU+Ryi2J3cNIooJ1iPdfzLN24/
13
- 7kGrCz0uFDhSLn373QLbv/Q7OXXxOlWniKebF68aBt0CgYEA4JdHM7mG+Pj+3I1A
14
- oV2JeQ/okMIkeAyi+YP2uz8ENK+fNkFumSgUOMihVq2RxYX9vbA31I6AZR2OehNL
15
- DJROr+5beofJgZFP1RqY0yVSByWPyKEXDoEyxYb0x3ymTDxEJxfchzNJHLs4zMRg
16
- HV6HEhF1/RstAQOrgMdpxCQiZV8CgYEA1b6awQClNav848iHCtq9VQbj7q9rqcio
17
- 7cVd8noh3crpTi+g89yEkNGUU9KBI5+CSQwJbgWZcmu6DVoZww/piebmDfNfs2Q9
18
- MYaJ3g1JG42lQx+sD99Mdeki7yRGnYa1E9EWHuh0RPav6io2/p2orQKYYPKzNeu7
19
- Gk+6CmRFQXsCgYBvXXQYCOoNsOInmP8wRMJzPlky1qHgyVtS4IXK6X97ZI5NB/sA
20
- hOgqFAo0gdKjoEJm+edlRdgSy6onjEONlEjEYiwIQSbBUb3FTO/02y+mjZCTipV1
21
- QslbOwVLmdqYONYOkH1wqRSa45dRqhqC1amiYvgfrocNVrWHINqoQMaxfwKBgQCQ
22
- UK46K85gS6xmu2j6WjBFbW6OJKDjtNjiGUVSR0YAc1VM9fmNTVGwWeD1K4cH31Xf
23
- LiJbx6oQSE4JX9FPE7pR4rVnq+/stnxeqQEDDSNUWDHB/Wt2GAWPAwcUQgDvsRkn
24
- tR9jZP3D5m9mAnS5Owe9MKhjpNp9SJk+rZ/mDkd4awKBgCBTqzV43mNNWW1ZTyAd
25
- zGWbVseAZI/i1SqYG59gIuEtPdeQYcF893V184U2CAaBIUarE6DENXqOuBOGPn3s
26
- 2K8hd+B+0d6zB8WwvnO4H9gaO6OZleEOV3pVaCOMlDn6GE8UiQoS6JzUXk3+iW4j
27
- jSA0Xr3pUFydKnSdlYLECucd
2
+ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCtsWofzLCShpyK
3
+ Y/OJ2HwUQngIfDJjeXWcFArXnoZF/sBYYqBMdZaACKnKrOjD3u6kHXkTrjmZRnog
4
+ 0f+l9Jpw7IMkeKlJ6yGkR0txBVhrRqdFkS7EISjb7cQBy+3RK8+waMUzuYFf8OnX
5
+ E97jzkX2cZGXZurdNbew8DxZLaKUI7vRPHnjASlSmjtUa3JWSpFhMn7hz98DaSBO
6
+ hsLB/Iwp+CPq/2PD4HfpOBMgmh/p4LRW0tJRIccbqk6T0WMkK5RAzGtYv5xE7jIi
7
+ 9lhZIxDtoBF/6cNORpTt2FEw6Wl1lFmX/gpOzGHr6xQwue6EmbzME5+aSa0M7bzS
8
+ YxXPmBepAgMBAAECggEBAIqgeMTch2jKyxGg6HTyNUWuL0MGbOj7vaROUsD4os4m
9
+ nrlsLegYSX/yaiF6k2QQ/4I4i7Prb8kneL3NHS8E5GaQPbLUIrj+UyFcTZfs3j7w
10
+ avyY/SxIEuZFBBUy/6HcR6zSUeIQgnNiQiAImfJTZX9l8P9Xgsf+4Zb0hhXe2E1G
11
+ TqLAYWnLoxD9us0rryQb37mRIREKcTO3qza+FXk32bWe83f1Dy46JJIChIoQg/QB
12
+ hwzSL51ld22fy7p+bsqeI3HUDWHplq4T0lws0SGUd4IvntF9rNGYHAI+aUKwiUBC
13
+ g/0se9+GBKeMSjhS11F9/Clg4i0E/tN/poOytA83aAECgYEA3+E0bhbaFAvzzevv
14
+ tAWSXkwHSFZ745KsJSJ8OOVkd+ATleyGl3AoRTrDxNqY+R/TvBFaTyiY+zqGV6tj
15
+ NI8SaSXuhRzbA9zDL2rSH7GIOeqzUs8PSUMk6JnmfCKk+aTBOoi8ULolHE4N6B45
16
+ te3GVgy/luJK22pPLCMsmL+D0ykCgYEAxpzr8T2CPVtl33W+loGbvrqZ1xXgq/7n
17
+ IsjHZdVdFsIIxFhtE2A2iO/7vMsLmwkZ7LgbtHcfEPOxjx5VzVt89l1I2lnP0jW+
18
+ pjKrN/eyXcHnWLLPCEtXA06oIvyem/VMSGXumu7EcOJZjnG3/PmqCWCjc2UUo2lJ
19
+ F2rQdC9bMIECgYEAl3nSdaI0j1e+79bw6kbSz8Z1LvaFAGce3klE72IV5h3QYqIU
20
+ NqaGOMEX8DtPQU/NfPPovKJlT6Y7e1nU15zuAgLOLXZmoWhfD9ggr5z45Obtydub
21
+ JiCt+ksW7WqrYNWef7JAaAZqUYpmUmUQ+w0UIuihQL9/kpGNW/m4lOkPknECgYAI
22
+ gbodWAwXAq4nVwy1t5FrJuTl8HryAvX1aHIZ63yUN/VWK49ocAuF6/l1SaESn94r
23
+ ZGtTXHLJMBbf0WXNaOi+SJqRN52OHF0xEySAPiy2lVKKWwZBDbEJZDoRXY6RkX0V
24
+ 8L+6hRWG3DsHvdkqjar5wdjeXWr34M+PoDSTdV/LgQKBgFAqJc4I91AK8Ah9Ba8k
25
+ LqYtRFGFJ2J+p8QqcibfT6d6tZqWffxiXbQ5bS52/fmM3BdxEHjtzLdA7cOqCh6g
26
+ 79kxRb3B5jjq5JvGtLy89VphZ08W9dhzgL/l3KGtgMobfTYUEDaNk33oExhItQnx
27
+ F97ecAMIx20oqFFOg6N0unwW
28
28
  -----END PRIVATE KEY-----
@@ -45,9 +45,9 @@ def step_impl(context, varname, format_value, duration, duration_unit, value):
45
45
  deltaT = timedelta(**args)
46
46
 
47
47
  if value == "ago":
48
- date = datetime.now() - deltaT
48
+ date = DateTime.now() - deltaT
49
49
  elif value == "ahead":
50
- date = datetime.now() + deltaT
50
+ date = DateTime.now() + deltaT
51
51
 
52
52
 
53
53
  v = date.strftime(format_value)
@@ -92,12 +92,12 @@ class RMQManager:
92
92
  # polling_seconds = 0.001 # a polling period below 1 ms is usually not efficient in testing context
93
93
  #
94
94
  # # Wait first message is needed
95
- # dt_begin = datetime.now()
95
+ # dt_begin = DateTime.now()
96
96
  # dt_last_poll = dt_begin
97
97
  # if first_timeout_seconds is not None:
98
98
  # while (dt_last_poll - dt_begin).total_seconds() < first_timeout_seconds:
99
99
  # nb_msg_by_consumer = {c.name: c.nb_messages for c in consumers if c.nb_messages > 0}
100
- # dt_last_poll = datetime.now()
100
+ # dt_last_poll = DateTime.now()
101
101
  # if len(nb_msg_by_consumer) > 0:
102
102
  # res = sum(nb_msg_by_consumer.values())
103
103
  # if Tools.do_log(logger, logging.DEBUG):
@@ -115,7 +115,7 @@ class RMQManager:
115
115
  # while (dt_last_poll - dt_last_receive).total_seconds() < window_seconds:
116
116
  # nb_msg_by_consumer = {c.name: c.nb_messages for c in consumers if c.nb_messages > 0}
117
117
  # nb = sum(nb_msg_by_consumer.values())
118
- # dt_last_poll = datetime.now()
118
+ # dt_last_poll = DateTime.now()
119
119
  # if nb > res:
120
120
  # res = nb
121
121
  # if Tools.do_log(logger, logging.DEBUG):
@@ -14,9 +14,10 @@
14
14
  from holado.common.context.session_context import SessionContext
15
15
  import logging
16
16
  from holado_core.common.resource.table_data_manager import TableDataManager
17
- from holado_python.common.tools.datetime import DateTime
17
+ from holado_python.common.tools.datetime import DateTime, TIMEZONE_LOCAL
18
18
  import os
19
19
  from holado_system.system.filesystem.file import File
20
+ import re
20
21
 
21
22
 
22
23
  logger = logging.getLogger(__name__)
@@ -99,7 +100,7 @@ class CampaignManager(object):
99
100
  res_dict_list = client.execute(query_str, result_as_dict_list=True, as_generator=False)
100
101
 
101
102
  status_dt_str = res_dict_list[0]['status_at'] if res_dict_list else None
102
- status_dt = DateTime.str_2_datetime(status_dt_str) if status_dt_str else None
103
+ status_dt = DateTime.str_2_datetime(status_dt_str, tz=TIMEZONE_LOCAL) if status_dt_str else None
103
104
  return status_dt
104
105
 
105
106
  def import_campaign_reports(self, report_path):
@@ -182,7 +183,11 @@ class CampaignManager(object):
182
183
  parts = line.split(' - ')
183
184
  status_dt_str = parts[0]
184
185
  scenario_name = parts[1]
185
- status = parts[-1]
186
+ status_info = parts[-1]
187
+
188
+ m = re.match(r"^(.*?)(?: \(.*\))?$", status_info)
189
+ status = m.group(1)
190
+
186
191
  self.update_or_add_campaign_scenario(camp_id, scenario_name, status=status, status_at_str=status_dt_str)
187
192
 
188
193
 
@@ -33,10 +33,10 @@ class DetailedScenarioFailedReportBuilder(ReportBuilder):
33
33
 
34
34
  def after_scenario(self, scenario, scenario_report=None):
35
35
  from holado_report.report.report_manager import ReportManager
36
- status_validation, step_failed, step_number = ReportManager.get_current_scenario_status_information(scenario)
36
+ category_validation, status_validation, step_failed, step_number = ReportManager.get_current_scenario_status_information(scenario)
37
37
 
38
38
  if status_validation != "Passed":
39
- self.__file_fail_add_scenario(scenario, scenario_report, status_validation, step_failed, step_number)
39
+ self.__file_fail_add_scenario(scenario, scenario_report, category_validation, status_validation, step_failed, step_number)
40
40
 
41
41
  def after_all(self):
42
42
  # Manage file fail
@@ -44,13 +44,13 @@ class DetailedScenarioFailedReportBuilder(ReportBuilder):
44
44
  self.__file_fail.close()
45
45
  self.__file_fail = None
46
46
 
47
- def __file_fail_add_scenario(self, scenario, scenario_report, status_validation, step_failed, step_number):
47
+ def __file_fail_add_scenario(self, scenario, scenario_report, category_validation, status_validation, step_failed, step_number):
48
48
  if self.__is_format_xml:
49
- self.__file_fail_add_scenario_xml(scenario, scenario_report, status_validation, step_failed, step_number)
49
+ self.__file_fail_add_scenario_xml(scenario, scenario_report, category_validation, status_validation, step_failed, step_number)
50
50
  else:
51
- self.__file_fail_add_scenario_txt(scenario, scenario_report, status_validation, step_failed, step_number)
51
+ self.__file_fail_add_scenario_txt(scenario, scenario_report, category_validation, status_validation, step_failed, step_number)
52
52
 
53
- def __file_fail_add_scenario_xml(self, scenario, scenario_report, status_validation, step_failed, step_number):
53
+ def __file_fail_add_scenario_xml(self, scenario, scenario_report, category_validation, status_validation, step_failed, step_number):
54
54
  from holado_report.report.report_manager import ReportManager
55
55
 
56
56
  self.__open_file_if_needed()
@@ -61,6 +61,8 @@ class DetailedScenarioFailedReportBuilder(ReportBuilder):
61
61
  msg_list.append(f" <scenario>{scenario.name}</scenario>")
62
62
  msg_list.append(f" <report>{scenario_report.report_path}</report>")
63
63
  msg_list.append(f" <tags>-t " + " -t ".join(scenario.feature.tags + scenario.tags) + "</tags>")
64
+ if category_validation:
65
+ msg_list.append(f" <validation_category>{category_validation}</validation_category>")
64
66
  msg_list.append(f" <validation_status>{status_validation}</validation_status>")
65
67
  if step_failed is not None:
66
68
  msg_list.append(f" <failure>")
@@ -102,7 +104,7 @@ class DetailedScenarioFailedReportBuilder(ReportBuilder):
102
104
  self.__file_fail.write(msg)
103
105
  self.__file_fail.flush()
104
106
 
105
- def __file_fail_add_scenario_txt(self, scenario, scenario_report, status_validation, step_failed, step_number):
107
+ def __file_fail_add_scenario_txt(self, scenario, scenario_report, category_validation, status_validation, step_failed, step_number):
106
108
  from holado_report.report.report_manager import ReportManager
107
109
 
108
110
  self.__open_file_if_needed()
@@ -112,6 +114,8 @@ class DetailedScenarioFailedReportBuilder(ReportBuilder):
112
114
  msg_list.append(f" Scenario: {scenario.name}")
113
115
  msg_list.append(f" Report: {scenario_report.report_path}")
114
116
  msg_list.append(f" Tags: -t " + " -t ".join(scenario.feature.tags + scenario.tags))
117
+ if category_validation:
118
+ msg_list.append(f" Validation category: {category_validation}")
115
119
  msg_list.append(f" Validation status: {status_validation}")
116
120
  if step_failed is not None:
117
121
  msg_list.append(f" Failure:")
@@ -41,16 +41,17 @@ class FailureReportBuilder(ReportBuilder):
41
41
 
42
42
  def after_scenario(self, scenario, scenario_report=None):
43
43
  from holado_report.report.report_manager import ReportManager
44
- status_validation, step_failed, step_number = ReportManager.get_current_scenario_status_information(scenario)
44
+ category_validation, status_validation, step_failed, step_number = ReportManager.get_current_scenario_status_information(scenario)
45
45
 
46
46
  if status_validation != "Passed":
47
47
  step_error_message = ReportManager.get_step_error_message(step_failed).strip()
48
48
  if step_error_message not in self.__failures:
49
49
  self.__failures[step_error_message] = []
50
50
 
51
+ category_str = f" ({category_validation})" if category_validation else ""
51
52
  if self.__file_format == 'txt':
52
53
  msg_list = []
53
- msg_list.append(f"scenario in {scenario.filename} at l.{scenario.line} - step {step_number} (l.{step_failed.line}) - {status_validation}")
54
+ msg_list.append(f"scenario in {scenario.filename} at l.{scenario.line} - step {step_number} (l.{step_failed.line}) - {status_validation}{category_str}")
54
55
  msg_list.append(f" Feature/Scenario: {scenario.feature.name} => {scenario.name}")
55
56
  msg_list.append(f" Report: {scenario_report.report_path}")
56
57
  msg_list.append(f" Tags: -t " + " -t ".join(scenario.feature.tags + scenario.tags))
@@ -59,7 +60,7 @@ class FailureReportBuilder(ReportBuilder):
59
60
  self.__failures[step_error_message].append(msg_scenario)
60
61
  elif self.__file_format in ['json', 'xml']:
61
62
  scenario_info = {
62
- 'title': f"{scenario.filename} - l.{scenario.line} - step {step_number} (l.{step_failed.line}) - {status_validation}",
63
+ 'title': f"{scenario.filename} - l.{scenario.line} - step {step_number} (l.{step_failed.line}) - {status_validation}{category_str}",
63
64
  'scenario': f"{scenario.feature.name} => {scenario.name}",
64
65
  'report': scenario_report.report_path,
65
66
  'tags': "-t " + " -t ".join(scenario.feature.tags + scenario.tags),
@@ -32,10 +32,10 @@ class ShortScenarioFailedReportBuilder(ReportBuilder):
32
32
 
33
33
  def after_scenario(self, scenario, scenario_report=None):
34
34
  from holado_report.report.report_manager import ReportManager
35
- status_validation, step_failed, step_number = ReportManager.get_current_scenario_status_information(scenario)
35
+ category_validation, status_validation, step_failed, step_number = ReportManager.get_current_scenario_status_information(scenario)
36
36
 
37
37
  if status_validation != "Passed":
38
- self.__file_fail_add_scenario(scenario, scenario_report, status_validation, step_failed, step_number)
38
+ self.__file_fail_add_scenario(scenario, scenario_report, category_validation, status_validation, step_failed, step_number)
39
39
 
40
40
  def after_all(self):
41
41
  # Manage file fail
@@ -43,16 +43,17 @@ class ShortScenarioFailedReportBuilder(ReportBuilder):
43
43
  self.__file_fail.close()
44
44
  self.__file_fail = None
45
45
 
46
- def __file_fail_add_scenario(self, scenario, scenario_report, status_validation, step_failed, step_number):
46
+ def __file_fail_add_scenario(self, scenario, scenario_report, category_validation, status_validation, step_failed, step_number):
47
47
  from holado_report.report.report_manager import ReportManager
48
48
 
49
49
  self.__open_file_if_needed()
50
50
 
51
51
  msg_list = []
52
+ category_str = f" ({category_validation})" if category_validation else ""
52
53
  if step_failed is not None:
53
- msg_list.append(f"scenario in {scenario.filename} at l.{scenario.line} - step {step_number} (l.{step_failed.line}) - {status_validation}")
54
+ msg_list.append(f"scenario in {scenario.filename} at l.{scenario.line} - step {step_number} (l.{step_failed.line}) - {status_validation}{category_str}")
54
55
  else:
55
- msg_list.append(f"scenario in {scenario.filename} at l.{scenario.line} - step ? (missing step implementation ?) - {status_validation}")
56
+ msg_list.append(f"scenario in {scenario.filename} at l.{scenario.line} - step ? (missing step implementation ?) - {status_validation}{category_str}")
56
57
  msg_list.append(f" Feature/Scenario: {scenario.feature.name} => {scenario.name}")
57
58
  msg_list.append(f" Report: {scenario_report.report_path}")
58
59
  msg_list.append(f" Tags: -t " + " -t ".join(scenario.feature.tags + scenario.tags))
@@ -63,7 +63,7 @@ class SummaryReportBuilder(ReportBuilder):
63
63
 
64
64
  def after_scenario(self, scenario, scenario_report=None):
65
65
  from holado_report.report.report_manager import ReportManager
66
- status_name, _, _ = ReportManager.get_current_scenario_status_information(scenario)
66
+ _, status_name, _, _ = ReportManager.get_current_scenario_status_information(scenario)
67
67
 
68
68
  if status_name not in self.__scenarios:
69
69
  self.__scenarios[status_name] = 0
@@ -31,10 +31,10 @@ class SummaryScenarioFailedReportBuilder(ReportBuilder):
31
31
 
32
32
  def after_scenario(self, scenario, scenario_report=None):
33
33
  from holado_report.report.report_manager import ReportManager
34
- status_validation, step_failed, step_number = ReportManager.get_current_scenario_status_information(scenario)
34
+ category_validation, status_validation, step_failed, step_number = ReportManager.get_current_scenario_status_information(scenario)
35
35
 
36
36
  if status_validation != "Passed":
37
- self.__file_fail_add_scenario(scenario, status_validation, step_failed, step_number)
37
+ self.__file_fail_add_scenario(scenario, category_validation, status_validation, step_failed, step_number)
38
38
 
39
39
  def after_all(self):
40
40
  # Manage file fail
@@ -42,12 +42,13 @@ class SummaryScenarioFailedReportBuilder(ReportBuilder):
42
42
  self.__file_fail.close()
43
43
  self.__file_fail = None
44
44
 
45
- def __file_fail_add_scenario(self, scenario, status_validation, step_failed, step_number):
45
+ def __file_fail_add_scenario(self, scenario, category_validation, status_validation, step_failed, step_number):
46
46
  self.__open_file_if_needed()
47
+ category_str = f" ({category_validation})" if category_validation else ""
47
48
  if step_failed is not None:
48
- self.__file_fail.write(f"scenario in {scenario.filename} at l.{scenario.line} - step {step_number} (l.{step_failed.line}) - {status_validation}\n")
49
+ self.__file_fail.write(f"scenario in {scenario.filename} at l.{scenario.line} - step {step_number} (l.{step_failed.line}) - {status_validation}{category_str}\n")
49
50
  else:
50
- self.__file_fail.write(f"scenario in {scenario.filename} at l.{scenario.line} - step ? (missing step implementation ?) - {status_validation}\n")
51
+ self.__file_fail.write(f"scenario in {scenario.filename} at l.{scenario.line} - step ? (missing step implementation ?) - {status_validation}{category_str}\n")
51
52
  self.__file_fail.flush()
52
53
 
53
54
  def __open_file_if_needed(self):
@@ -33,12 +33,12 @@ class SummaryScenarioReportBuilder(ReportBuilder):
33
33
 
34
34
  def after_scenario(self, scenario, scenario_report=None):
35
35
  from holado_report.report.report_manager import ReportManager
36
- status_validation, step_failed, step_number = ReportManager.get_current_scenario_status_information(scenario)
36
+ category_validation, status_validation, step_failed, step_number = ReportManager.get_current_scenario_status_information(scenario)
37
37
 
38
38
  if status_validation == "Passed":
39
- self.__file_add_scenario_success(scenario, status_validation)
39
+ self.__file_add_scenario_success(scenario, category_validation, status_validation)
40
40
  else:
41
- self.__file_add_scenario_fail(scenario, status_validation, step_failed, step_number)
41
+ self.__file_add_scenario_fail(scenario, category_validation, status_validation, step_failed, step_number)
42
42
 
43
43
  def after_all(self):
44
44
  # Manage file fail
@@ -46,27 +46,28 @@ class SummaryScenarioReportBuilder(ReportBuilder):
46
46
  self.__file.close()
47
47
  self.__file = None
48
48
 
49
- def __file_add_scenario_success(self, scenario, status_validation):
49
+ def __file_add_scenario_success(self, scenario, category_validation, status_validation):
50
50
  self.__open_file_if_needed()
51
- self.__file_write(scenario, None, status_validation)
51
+ self.__file_write(scenario, None, category_validation, status_validation)
52
52
  self.__file.flush()
53
53
 
54
- def __file_add_scenario_fail(self, scenario, status_validation, step_failed, step_number):
54
+ def __file_add_scenario_fail(self, scenario, category_validation, status_validation, step_failed, step_number):
55
55
  self.__open_file_if_needed()
56
56
  if step_failed is not None:
57
- self.__file_write(scenario, f"step {step_number} (l.{step_failed.line})", status_validation)
57
+ self.__file_write(scenario, f"step {step_number} (l.{step_failed.line})", category_validation, status_validation)
58
58
  else:
59
- self.__file_write(scenario, f"step ? (missing step implementation ?)", status_validation)
59
+ self.__file_write(scenario, f"step ? (missing step implementation ?)", category_validation, status_validation)
60
60
  self.__file.flush()
61
61
 
62
- def __file_write(self, scenario, text, status_validation):
62
+ def __file_write(self, scenario, text, category_validation, status_validation):
63
63
  dt = DateTime.now()
64
64
  dt_str = DateTime.datetime_2_str(dt, FORMAT_DATETIME_HUMAN_SECOND)
65
65
 
66
+ category_str = f" ({category_validation})" if category_validation else ""
66
67
  if text:
67
- self.__file.write(f"{dt_str} - {scenario.filename} at l.{scenario.line} - {text} - {status_validation}\n")
68
+ self.__file.write(f"{dt_str} - {scenario.filename} at l.{scenario.line} - {text} - {status_validation}{category_str}\n")
68
69
  else:
69
- self.__file.write(f"{dt_str} - {scenario.filename} at l.{scenario.line} - {status_validation}\n")
70
+ self.__file.write(f"{dt_str} - {scenario.filename} at l.{scenario.line} - {status_validation}{category_str}\n")
70
71
 
71
72
  def __open_file_if_needed(self):
72
73
  if self.__file is None:
@@ -56,11 +56,12 @@ class ExecutionHistoric():
56
56
  self.__get_execution_historic_current_feature_scenarios().append(seh)
57
57
 
58
58
  def __new_ScenarioExecutionHistoric(self, scenario_context, scenario, scenario_report):
59
- res = NamedTuple("ScenarioExecutionHistoric", scenario_context=object, scenario=object, scenario_report=object, steps_by_scope=dict, status_validation=str, step_failed=object, step_failed_number=int)
59
+ res = NamedTuple("ScenarioExecutionHistoric", scenario_context=object, scenario=object, scenario_report=object, steps_by_scope=dict, category_validation=str, status_validation=str, step_failed=object, step_failed_number=int)
60
60
  res.scenario_context = scenario_context
61
61
  res.scenario = scenario
62
62
  res.scenario_report = scenario_report
63
63
  res.steps_by_scope = {}
64
+ res.category_validation = None
64
65
  res.status_validation = None
65
66
  res.step_failed = None
66
67
  res.step_failed_number = None
@@ -87,10 +88,11 @@ class ExecutionHistoric():
87
88
 
88
89
  def after_scenario(self, scenario, scenario_report=None):
89
90
  from holado_report.report.report_manager import ReportManager
90
- status_validation, step_failed, step_number = ReportManager.get_current_scenario_status_information(scenario)
91
+ category_validation, status_validation, step_failed, step_number = ReportManager.get_current_scenario_status_information(scenario)
91
92
 
92
93
  # Update execution historic
93
94
  current_scenario = self.__get_execution_historic_current_scenario()
95
+ current_scenario.category_validation = category_validation
94
96
  current_scenario.status_validation = status_validation
95
97
  current_scenario.step_failed = step_failed
96
98
  current_scenario.step_failed_number = step_number