holado 0.9.0__py3-none-any.whl → 0.9.2__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.
- holado/__init__.py +26 -5
- holado/common/context/session_context.py +8 -5
- holado/holado_config.py +0 -3
- {holado-0.9.0.dist-info → holado-0.9.2.dist-info}/METADATA +1 -1
- {holado-0.9.0.dist-info → holado-0.9.2.dist-info}/RECORD +48 -48
- holado_core/common/block/base.py +3 -3
- holado_core/common/tools/path_manager.py +1 -1
- holado_django/server/django_projects/rest_api/rest_api/urls.py +1 -1
- holado_docker/tests/behave/steps/tools/docker_controller/client_steps.py +2 -2
- holado_docker/tools/docker_controller/client/rest/docker_controller_client.py +38 -1
- holado_docker/tools/docker_controller/server/run_docker_controller_in_docker.sh +3 -3
- holado_examples/tests/behave/testing_solution/environment.py +3 -3
- holado_examples/tests/behave/testing_solution/src/context/session_context.py +2 -2
- holado_examples/tests/behave/testing_solution/steps/config_steps.py +1 -1
- holado_grpc/__init__.py +8 -0
- holado_helper/holado_module_template/__init__.py +7 -0
- holado_logging/__init__.py +8 -4
- holado_logging/common/logging/log_config.py +13 -4
- holado_multitask/multithreading/loopthread.py +4 -4
- holado_python/__init__.py +1 -0
- holado_python/common/tools/datetime.py +36 -17
- holado_python/standard_library/ssl/resources/certificates/tcpbin.crt +16 -16
- holado_python/standard_library/ssl/resources/certificates/tcpbin.key +26 -26
- holado_python/tests/behave/steps/standard_library/datetime_steps.py +2 -2
- holado_rabbitmq/tools/rabbitmq/rabbitmq_manager.py +3 -3
- holado_report/__init__.py +6 -0
- holado_report/campaign/campaign_manager.py +8 -3
- holado_report/report/builders/detailed_scenario_failed_report_builder.py +11 -7
- holado_report/report/builders/failure_report_builder.py +4 -3
- holado_report/report/builders/short_scenario_failed_report_builder.py +6 -5
- holado_report/report/builders/summary_report_builder.py +1 -1
- holado_report/report/builders/summary_scenario_failed_report_builder.py +6 -5
- holado_report/report/builders/summary_scenario_report_builder.py +12 -11
- holado_report/report/execution_historic.py +4 -2
- holado_report/report/report_manager.py +63 -13
- holado_rest/__init__.py +8 -0
- holado_rest/api/rest/rest_client.py +5 -1
- holado_test/__init__.py +3 -0
- holado_test/common/context/feature_context.py +3 -3
- holado_test/common/context/scenario_context.py +3 -3
- holado_test/common/context/step_context.py +3 -3
- holado_test/test_server/client/rest/test_server_client.py +125 -0
- holado_test/test_server/server/run_test_server_in_docker.sh +7 -7
- holado_value/common/tools/unique_value_manager.py +2 -1
- test_holado/environment.py +1 -1
- test_holado/logging.conf +1 -0
- holado_docker/tools/docker_controller/docker_controller_manager.py +0 -46
- {holado-0.9.0.dist-info → holado-0.9.2.dist-info}/WHEEL +0 -0
- {holado-0.9.0.dist-info → holado-0.9.2.dist-info}/licenses/LICENSE +0 -0
holado_logging/__init__.py
CHANGED
|
@@ -14,13 +14,17 @@
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
from holado_logging.common.logging.log_config import LogConfig
|
|
19
|
-
LogConfig.configure(use_holado_logger=use_holado_logger, config_file_path=logging_config_file_path, log_level=log_level, log_on_console=log_on_console, log_in_file=log_in_file)
|
|
20
|
-
|
|
17
|
+
|
|
21
18
|
def dependencies():
|
|
22
19
|
return None
|
|
23
20
|
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def configure(use_holado_logger=True, logging_config_file_path=None, log_level=None, log_time_in_utc=None, log_on_console=False, log_in_file=True):
|
|
24
|
+
from holado_logging.common.logging.log_config import LogConfig
|
|
25
|
+
LogConfig.configure(use_holado_logger=use_holado_logger, config_file_path=logging_config_file_path,
|
|
26
|
+
log_level=log_level, log_time_in_utc=log_time_in_utc, log_on_console=log_on_console, log_in_file=log_in_file)
|
|
27
|
+
|
|
24
28
|
def initialize_and_register():
|
|
25
29
|
from holado.common.context.session_context import SessionContext
|
|
26
30
|
from holado_logging.common.logging.log_manager import LogManager
|
|
@@ -22,12 +22,13 @@ class LogConfig(object):
|
|
|
22
22
|
TManager = None
|
|
23
23
|
config_file_path = None
|
|
24
24
|
default_level = logging.INFO
|
|
25
|
+
log_time_in_utc = True
|
|
25
26
|
log_on_console=False
|
|
26
27
|
log_in_file=True
|
|
27
28
|
__is_configured = False
|
|
28
29
|
|
|
29
30
|
@classmethod
|
|
30
|
-
def configure(cls, use_holado_logger=True, config_file_path=None, log_level=None, log_on_console=False, log_in_file=True):
|
|
31
|
+
def configure(cls, use_holado_logger=True, config_file_path=None, log_level=None, log_time_in_utc=None, log_on_console=False, log_in_file=True):
|
|
31
32
|
if cls.__is_configured:
|
|
32
33
|
logging.warning(f"Logging was already configured, it is not possible to configure it twice. This new configuration is skipped.")
|
|
33
34
|
return
|
|
@@ -45,14 +46,17 @@ class LogConfig(object):
|
|
|
45
46
|
import configparser
|
|
46
47
|
config = configparser.ConfigParser()
|
|
47
48
|
config.read(config_file_path)
|
|
48
|
-
log_level = config.get("holado", "level")
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
log_level = config.get("holado", "level", fallback=log_level)
|
|
50
|
+
log_time_in_utc = config.getboolean("holado", "log_time_in_utc", fallback=log_time_in_utc)
|
|
51
|
+
log_on_console = config.get("holado", "log_on_console", fallback=log_on_console)
|
|
52
|
+
log_in_file = config.get("holado", "log_in_file", fallback=log_in_file)
|
|
51
53
|
|
|
52
54
|
if log_level:
|
|
53
55
|
if isinstance(log_level, str):
|
|
54
56
|
log_level = logging._nameToLevel[log_level]
|
|
55
57
|
cls.default_level = log_level
|
|
58
|
+
if log_time_in_utc is not None:
|
|
59
|
+
cls.log_time_in_utc = log_time_in_utc
|
|
56
60
|
if log_on_console:
|
|
57
61
|
if isinstance(log_on_console, str):
|
|
58
62
|
log_on_console = True if log_on_console == "True" else False
|
|
@@ -62,6 +66,11 @@ class LogConfig(object):
|
|
|
62
66
|
log_in_file = True if log_in_file == "True" else False
|
|
63
67
|
cls.log_in_file = log_in_file
|
|
64
68
|
|
|
69
|
+
# Change log time format if needed
|
|
70
|
+
if cls.log_time_in_utc:
|
|
71
|
+
import time
|
|
72
|
+
logging.Formatter.converter = time.gmtime
|
|
73
|
+
|
|
65
74
|
cls.__is_configured = True
|
|
66
75
|
|
|
67
76
|
@classmethod
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
|
|
14
14
|
import threading
|
|
15
15
|
import time
|
|
16
|
-
import datetime
|
|
17
16
|
import logging
|
|
18
17
|
import holado_multitask.multithreading.thread as ctt
|
|
19
18
|
from holado.common.handlers.undefined import default_context
|
|
19
|
+
from holado_python.common.tools.datetime import DateTime
|
|
20
20
|
|
|
21
21
|
logger = logging.getLogger(__name__)
|
|
22
22
|
|
|
@@ -31,7 +31,7 @@ class LoopThread(ctt.InterruptableThread):
|
|
|
31
31
|
|
|
32
32
|
# Manage loops
|
|
33
33
|
self.__is_under_loop = threading.Event()
|
|
34
|
-
self.__next_loop_datetime =
|
|
34
|
+
self.__next_loop_datetime = DateTime.now()
|
|
35
35
|
self.__loop_counter = 0
|
|
36
36
|
|
|
37
37
|
@property
|
|
@@ -79,10 +79,10 @@ class LoopThread(ctt.InterruptableThread):
|
|
|
79
79
|
logger.info("Loop thread '{}' has finished.".format(self.name))
|
|
80
80
|
|
|
81
81
|
def does_need_run_loop(self):
|
|
82
|
-
return
|
|
82
|
+
return DateTime.now() >= self.__next_loop_datetime
|
|
83
83
|
|
|
84
84
|
def _get_sleep_time_before_next_loop(self):
|
|
85
|
-
now =
|
|
85
|
+
now = DateTime.now()
|
|
86
86
|
if now < self.__next_loop_datetime:
|
|
87
87
|
delta = self.__next_loop_datetime - now
|
|
88
88
|
res = delta.total_seconds()
|
holado_python/__init__.py
CHANGED
|
@@ -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
|
|
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,10 @@ 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
|
+
default_timezone = TIMEZONE_UTC
|
|
45
|
+
|
|
42
46
|
|
|
43
47
|
class DurationUnit(str, Enum):
|
|
44
48
|
# Nanosecond = "nanosecond"
|
|
@@ -55,11 +59,16 @@ class DurationUnit(str, Enum):
|
|
|
55
59
|
|
|
56
60
|
|
|
57
61
|
class DateTime(object):
|
|
62
|
+
""" Tools to manipulate datetimes.
|
|
63
|
+
Note: Naive and aware datetimes cannot be compared. Thus HolAdo methods always set datetime timezone when it is known.
|
|
64
|
+
In methods unable to define the timezone (ex: str_2_datetime), it is highly recommended to specify 'tz' argument.
|
|
65
|
+
"""
|
|
58
66
|
|
|
59
67
|
__is_system_time_in_tai = None
|
|
60
68
|
__is_system_time_expected_in_tai = None
|
|
61
69
|
__utc_to_tai_timedelta = None
|
|
62
70
|
|
|
71
|
+
|
|
63
72
|
def __init__(self):
|
|
64
73
|
pass
|
|
65
74
|
|
|
@@ -71,22 +80,22 @@ class DateTime(object):
|
|
|
71
80
|
return cls.__is_system_time_in_tai
|
|
72
81
|
|
|
73
82
|
@classmethod
|
|
74
|
-
def now(cls):
|
|
75
|
-
return datetime.datetime.now()
|
|
83
|
+
def now(cls, tz=default_timezone): # @UndefinedVariable
|
|
84
|
+
return datetime.datetime.now(tz=tz)
|
|
76
85
|
|
|
77
86
|
@classmethod
|
|
78
87
|
def utcnow(cls):
|
|
79
88
|
if cls.is_system_time_in_tai():
|
|
80
|
-
return
|
|
89
|
+
return cls.now(TIMEZONE_UTC) - cls.utc_to_tai_timedelta()
|
|
81
90
|
else:
|
|
82
|
-
return
|
|
91
|
+
return cls.now(TIMEZONE_UTC)
|
|
83
92
|
|
|
84
93
|
@classmethod
|
|
85
94
|
def tainow(cls):
|
|
86
95
|
if cls.is_system_time_in_tai():
|
|
87
|
-
return
|
|
96
|
+
return cls.now(TIMEZONE_UTC)
|
|
88
97
|
else:
|
|
89
|
-
return
|
|
98
|
+
return cls.now(TIMEZONE_UTC) + cls.utc_to_tai_timedelta()
|
|
90
99
|
|
|
91
100
|
@classmethod
|
|
92
101
|
def utc_to_tai_timedelta(cls):
|
|
@@ -126,8 +135,15 @@ class DateTime(object):
|
|
|
126
135
|
return res
|
|
127
136
|
|
|
128
137
|
@classmethod
|
|
129
|
-
def str_2_datetime(cls, dt_str, dt_format=None,
|
|
130
|
-
"""Convert string to datetime instance
|
|
138
|
+
def str_2_datetime(cls, dt_str, dt_format=None, tz=None):
|
|
139
|
+
"""Convert string to datetime instance
|
|
140
|
+
@param dt_format If defined, use this format to parse string rather than using first matching format
|
|
141
|
+
@param tz If defined, return a datetime with this timezone.
|
|
142
|
+
If string already defines a timezone, the datetime is converted to this timezone,
|
|
143
|
+
else the string is supposed to represent a datetime in this timezone and timezone is set in result datetime.
|
|
144
|
+
Note: Naive and aware datetimes cannot be compared. Thus HolAdo methods always set datetime timezone when it is known.
|
|
145
|
+
It is highly recommended to specify 'tz' argument
|
|
146
|
+
"""
|
|
131
147
|
res = None
|
|
132
148
|
if dt_format is None:
|
|
133
149
|
res = dateutil.parser.parse(dt_str)
|
|
@@ -135,8 +151,11 @@ class DateTime(object):
|
|
|
135
151
|
res = datetime.datetime.strptime(dt_str, dt_format)
|
|
136
152
|
|
|
137
153
|
# Set timezone if needed
|
|
138
|
-
if
|
|
139
|
-
|
|
154
|
+
if tz is not None:
|
|
155
|
+
if res.tzinfo is None:
|
|
156
|
+
res = res.replace(tzinfo=tz)
|
|
157
|
+
else:
|
|
158
|
+
res = res.astimezone(tz)
|
|
140
159
|
|
|
141
160
|
return res
|
|
142
161
|
|
|
@@ -162,19 +181,19 @@ class DateTime(object):
|
|
|
162
181
|
return res
|
|
163
182
|
|
|
164
183
|
@classmethod
|
|
165
|
-
def _get_datetime(cls, dt):
|
|
184
|
+
def _get_datetime(cls, dt, tz_in_str=None):
|
|
166
185
|
res = None
|
|
167
186
|
if dt is not None:
|
|
168
187
|
if isinstance(dt, datetime.datetime):
|
|
169
188
|
res = dt
|
|
170
189
|
elif isinstance(dt, str):
|
|
171
|
-
res = DateTime.str_2_datetime(dt)
|
|
190
|
+
res = DateTime.str_2_datetime(dt, tz=tz_in_str)
|
|
172
191
|
else:
|
|
173
192
|
raise TechnicalException(f"Datetime can be a datetime, a str containing a datetime, or None")
|
|
174
193
|
return res
|
|
175
194
|
|
|
176
195
|
@classmethod
|
|
177
|
-
def _get_epoch_datetime(cls, epoch):
|
|
196
|
+
def _get_epoch_datetime(cls, epoch, tz_in_str=None):
|
|
178
197
|
res = None
|
|
179
198
|
if epoch is not None:
|
|
180
199
|
if isinstance(epoch, datetime.datetime):
|
|
@@ -183,7 +202,7 @@ class DateTime(object):
|
|
|
183
202
|
if epoch in ['EPOCH_1970', 'EPOCH_JULIAN_CNES', 'EPOCH_JULIAN_NASA']:
|
|
184
203
|
res = eval(epoch)
|
|
185
204
|
else:
|
|
186
|
-
res = DateTime.str_2_datetime(epoch)
|
|
205
|
+
res = DateTime.str_2_datetime(epoch, tz=tz_in_str)
|
|
187
206
|
else:
|
|
188
207
|
raise TechnicalException(f"Epoch can be a datetime, a str containing a datetime, a str containing an EPOCH name, or None")
|
|
189
208
|
return res
|
|
@@ -296,7 +315,7 @@ class DateTime(object):
|
|
|
296
315
|
"""
|
|
297
316
|
dt_src = dt
|
|
298
317
|
if isinstance(dt_src, str):
|
|
299
|
-
dt_src = cls.str_2_datetime(dt_src, dt_format)
|
|
318
|
+
dt_src = cls.str_2_datetime(dt_src, dt_format, tz=TIMEZONE_UTC)
|
|
300
319
|
|
|
301
320
|
return dt_src + DateTime.utc_to_tai_timedelta()
|
|
302
321
|
|
|
@@ -308,7 +327,7 @@ class DateTime(object):
|
|
|
308
327
|
"""
|
|
309
328
|
dt_src = dt
|
|
310
329
|
if isinstance(dt_src, str):
|
|
311
|
-
dt_src = cls.str_2_datetime(dt_src, dt_format)
|
|
330
|
+
dt_src = cls.str_2_datetime(dt_src, dt_format, tz=TIMEZONE_UTC)
|
|
312
331
|
|
|
313
332
|
return dt_src - DateTime.utc_to_tai_timedelta()
|
|
314
333
|
|
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
MIIDZTCCAk2gAwIBAgIBKjANBgkqhkiG9w0BAQsFADCBizELMAkGA1UEBhMCVVMx
|
|
3
3
|
CzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQ8wDQYDVQQKDAZ0
|
|
4
4
|
Y3BiaW4xDDAKBgNVBAsMA29wczETMBEGA1UEAwwKdGNwYmluLmNvbTEjMCEGCSqG
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
FOLuMowBSAZfV5v82LmlaIIOvU/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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 =
|
|
48
|
+
date = DateTime.now() - deltaT
|
|
49
49
|
elif value == "ahead":
|
|
50
|
-
date =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
118
|
+
# dt_last_poll = DateTime.now()
|
|
119
119
|
# if nb > res:
|
|
120
120
|
# res = nb
|
|
121
121
|
# if Tools.do_log(logger, logging.DEBUG):
|
holado_report/__init__.py
CHANGED
|
@@ -13,6 +13,12 @@
|
|
|
13
13
|
#################################################
|
|
14
14
|
|
|
15
15
|
|
|
16
|
+
def configure_module():
|
|
17
|
+
from holado.holado_config import Config
|
|
18
|
+
from holado_python.common.tools.datetime import TIMEZONE_UTC
|
|
19
|
+
|
|
20
|
+
Config.report_timezone = TIMEZONE_UTC
|
|
21
|
+
|
|
16
22
|
def dependencies():
|
|
17
23
|
return ["holado_multitask"]
|
|
18
24
|
|
|
@@ -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
|
-
|
|
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):
|