testit-python-commons 4.2.8__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.
- api_client_syncstorage/__init__.py +27 -0
- api_client_syncstorage/api/__init__.py +3 -0
- api_client_syncstorage/api/completion_api.py +302 -0
- api_client_syncstorage/api/health_api.py +158 -0
- api_client_syncstorage/api/system_api.py +158 -0
- api_client_syncstorage/api/test_results_api.py +182 -0
- api_client_syncstorage/api/workers_api.py +308 -0
- api_client_syncstorage/api_client.py +898 -0
- api_client_syncstorage/apis/__init__.py +21 -0
- api_client_syncstorage/configuration.py +450 -0
- api_client_syncstorage/exceptions.py +158 -0
- api_client_syncstorage/model/__init__.py +5 -0
- api_client_syncstorage/model/completion_response.py +267 -0
- api_client_syncstorage/model/health_status_response.py +267 -0
- api_client_syncstorage/model/register_request.py +275 -0
- api_client_syncstorage/model/register_response.py +279 -0
- api_client_syncstorage/model/set_worker_status_request.py +271 -0
- api_client_syncstorage/model/set_worker_status_response.py +267 -0
- api_client_syncstorage/model/shutdown_response.py +267 -0
- api_client_syncstorage/model/test_result_cut_api_model.py +279 -0
- api_client_syncstorage/model/test_result_save_response.py +267 -0
- api_client_syncstorage/model_utils.py +2058 -0
- api_client_syncstorage/models/__init__.py +20 -0
- api_client_syncstorage/rest.py +356 -0
- testit.py +69 -0
- testit_python_commons/__init__.py +0 -0
- testit_python_commons/app_properties.py +257 -0
- testit_python_commons/client/__init__.py +9 -0
- testit_python_commons/client/api_client.py +560 -0
- testit_python_commons/client/client_configuration.py +74 -0
- testit_python_commons/client/converter.py +618 -0
- testit_python_commons/client/helpers/__init__.py +0 -0
- testit_python_commons/client/helpers/bulk_autotest_helper.py +246 -0
- testit_python_commons/client/helpers/threads_manager.py +86 -0
- testit_python_commons/client/models/__init__.py +8 -0
- testit_python_commons/client/models/threads_for_create_and_result.py +35 -0
- testit_python_commons/client/models/threads_for_update_and_result.py +45 -0
- testit_python_commons/configurations/__init__.py +0 -0
- testit_python_commons/configurations/properties_names.py +51 -0
- testit_python_commons/decorators.py +221 -0
- testit_python_commons/dynamic_methods.py +237 -0
- testit_python_commons/models/__init__.py +5 -0
- testit_python_commons/models/adapter_mode.py +4 -0
- testit_python_commons/models/fixture.py +24 -0
- testit_python_commons/models/link.py +53 -0
- testit_python_commons/models/link_type.py +10 -0
- testit_python_commons/models/outcome_type.py +5 -0
- testit_python_commons/models/status_type.py +6 -0
- testit_python_commons/models/step_result.py +90 -0
- testit_python_commons/models/test_result.py +297 -0
- testit_python_commons/models/test_result_with_all_fixture_step_results_model.py +28 -0
- testit_python_commons/services/__init__.py +41 -0
- testit_python_commons/services/adapter_manager.py +318 -0
- testit_python_commons/services/adapter_manager_configuration.py +42 -0
- testit_python_commons/services/fixture_manager.py +60 -0
- testit_python_commons/services/fixture_storage.py +40 -0
- testit_python_commons/services/logger.py +32 -0
- testit_python_commons/services/plugin_manager.py +101 -0
- testit_python_commons/services/retry.py +92 -0
- testit_python_commons/services/step_manager.py +36 -0
- testit_python_commons/services/step_result_storage.py +26 -0
- testit_python_commons/services/sync_storage/__init__.py +9 -0
- testit_python_commons/services/sync_storage/sync_storage_runner.py +589 -0
- testit_python_commons/services/utils.py +132 -0
- testit_python_commons/step.py +101 -0
- testit_python_commons/utils/__init__.py +3 -0
- testit_python_commons/utils/html_escape_utils.py +219 -0
- testit_python_commons-4.2.8.dist-info/METADATA +52 -0
- testit_python_commons-4.2.8.dist-info/RECORD +71 -0
- testit_python_commons-4.2.8.dist-info/WHEEL +5 -0
- testit_python_commons-4.2.8.dist-info/top_level.txt +3 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from testit_python_commons.models.adapter_mode import AdapterMode
|
|
2
|
+
from testit_python_commons.services.logger import adapter_logger
|
|
3
|
+
from testit_python_commons.services.utils import Utils
|
|
4
|
+
from testit_python_commons.configurations.properties_names import PropertiesNames
|
|
5
|
+
|
|
6
|
+
class AdapterManagerConfiguration:
|
|
7
|
+
__test_run_id = None
|
|
8
|
+
|
|
9
|
+
def __init__(self, app_properties: dict):
|
|
10
|
+
if app_properties.get(PropertiesNames.TEST_RUN_ID):
|
|
11
|
+
self.__test_run_id = Utils.uuid_check(app_properties.get(PropertiesNames.TEST_RUN_ID))
|
|
12
|
+
|
|
13
|
+
self.__adapter_mode = app_properties.get(PropertiesNames.ADAPTER_MODE, AdapterMode.USE_FILTER)
|
|
14
|
+
self.__automatic_creation_test_cases = Utils.convert_value_str_to_bool(
|
|
15
|
+
app_properties.get(PropertiesNames.AUTOMATIC_CREATION_TEST_CASES).lower())
|
|
16
|
+
|
|
17
|
+
self.__import_realtime = Utils.convert_value_str_to_bool(app_properties.get(PropertiesNames.IMPORT_REALTIME).lower())
|
|
18
|
+
self.__test_run_name = app_properties.get(PropertiesNames.TEST_RUN_NAME)
|
|
19
|
+
|
|
20
|
+
@adapter_logger
|
|
21
|
+
def get_test_run_id(self):
|
|
22
|
+
return self.__test_run_id
|
|
23
|
+
|
|
24
|
+
@adapter_logger
|
|
25
|
+
def set_test_run_id(self, test_run_id: str):
|
|
26
|
+
self.__test_run_id = test_run_id
|
|
27
|
+
|
|
28
|
+
@adapter_logger
|
|
29
|
+
def get_test_run_name(self):
|
|
30
|
+
return self.__test_run_name
|
|
31
|
+
|
|
32
|
+
@adapter_logger
|
|
33
|
+
def get_mode(self):
|
|
34
|
+
return self.__adapter_mode
|
|
35
|
+
|
|
36
|
+
@adapter_logger
|
|
37
|
+
def should_automatic_creation_test_cases(self) -> bool:
|
|
38
|
+
return self.__automatic_creation_test_cases
|
|
39
|
+
|
|
40
|
+
@adapter_logger
|
|
41
|
+
def should_import_realtime(self) -> bool:
|
|
42
|
+
return self.__import_realtime
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from testit_python_commons.services.fixture_storage import ThreadContextFixtures
|
|
2
|
+
from testit_python_commons.models.step_result import StepResult
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class FixtureManager:
|
|
6
|
+
def __init__(self):
|
|
7
|
+
self._items = ThreadContextFixtures()
|
|
8
|
+
self._orphan_items = []
|
|
9
|
+
|
|
10
|
+
def _update_item(self, uuid, **kwargs):
|
|
11
|
+
item = self._items[uuid] if uuid else self._items[next(reversed(self._items))]
|
|
12
|
+
for name, value in kwargs.items():
|
|
13
|
+
attr = getattr(item, name)
|
|
14
|
+
if isinstance(attr, list):
|
|
15
|
+
attr.append(value)
|
|
16
|
+
else:
|
|
17
|
+
setattr(item, name, value)
|
|
18
|
+
|
|
19
|
+
def _last_executable(self):
|
|
20
|
+
for _uuid in reversed(self._items):
|
|
21
|
+
if isinstance(self._items[_uuid], StepResult):
|
|
22
|
+
return _uuid
|
|
23
|
+
|
|
24
|
+
def get_item(self, uuid):
|
|
25
|
+
return self._items.get(uuid)
|
|
26
|
+
|
|
27
|
+
def get_last_item(self, item_type=None):
|
|
28
|
+
for _uuid in reversed(self._items):
|
|
29
|
+
if item_type is None:
|
|
30
|
+
return self._items.get(_uuid)
|
|
31
|
+
if type(self._items[_uuid]) == item_type:
|
|
32
|
+
return self._items.get(_uuid)
|
|
33
|
+
|
|
34
|
+
def start_group(self, uuid, group):
|
|
35
|
+
self._items[uuid] = group
|
|
36
|
+
|
|
37
|
+
def stop_group(self, uuid, **kwargs):
|
|
38
|
+
self._update_item(uuid, **kwargs)
|
|
39
|
+
|
|
40
|
+
def update_group(self, uuid, **kwargs):
|
|
41
|
+
self._update_item(uuid, **kwargs)
|
|
42
|
+
|
|
43
|
+
def start_before_fixture(self, parent_uuid, uuid, fixture):
|
|
44
|
+
self._items.get(parent_uuid).befores.append(fixture)
|
|
45
|
+
self._items[uuid] = fixture
|
|
46
|
+
|
|
47
|
+
def stop_before_fixture(self, uuid, **kwargs):
|
|
48
|
+
self._update_item(uuid, **kwargs)
|
|
49
|
+
self._items.pop(uuid)
|
|
50
|
+
|
|
51
|
+
def start_after_fixture(self, parent_uuid, uuid, fixture):
|
|
52
|
+
self._items.get(parent_uuid).afters.append(fixture)
|
|
53
|
+
self._items[uuid] = fixture
|
|
54
|
+
|
|
55
|
+
def stop_after_fixture(self, uuid, **kwargs):
|
|
56
|
+
self._update_item(uuid, **kwargs)
|
|
57
|
+
self._items.pop(uuid)
|
|
58
|
+
|
|
59
|
+
def get_all_items(self):
|
|
60
|
+
return self._items.get_all()
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
from collections import OrderedDict, defaultdict
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ThreadContextFixtures:
|
|
6
|
+
_thread_context = defaultdict(OrderedDict)
|
|
7
|
+
_init_thread: threading.Thread
|
|
8
|
+
|
|
9
|
+
@property
|
|
10
|
+
def thread_context(self):
|
|
11
|
+
context = self._thread_context[threading.current_thread()]
|
|
12
|
+
if not context and threading.current_thread() is not self._init_thread:
|
|
13
|
+
uuid, last_item = next(reversed(self._thread_context[self._init_thread].items()))
|
|
14
|
+
context[uuid] = last_item
|
|
15
|
+
return context
|
|
16
|
+
|
|
17
|
+
def __init__(self, *args, **kwargs):
|
|
18
|
+
self._init_thread = threading.current_thread()
|
|
19
|
+
super().__init__(*args, **kwargs)
|
|
20
|
+
|
|
21
|
+
def __setitem__(self, key, value):
|
|
22
|
+
self.thread_context.__setitem__(key, value)
|
|
23
|
+
|
|
24
|
+
def __getitem__(self, item):
|
|
25
|
+
return self.thread_context.__getitem__(item)
|
|
26
|
+
|
|
27
|
+
def __iter__(self):
|
|
28
|
+
return self.thread_context.__iter__()
|
|
29
|
+
|
|
30
|
+
def __reversed__(self):
|
|
31
|
+
return self.thread_context.__reversed__()
|
|
32
|
+
|
|
33
|
+
def get(self, key):
|
|
34
|
+
return self.thread_context.get(key)
|
|
35
|
+
|
|
36
|
+
def pop(self, key):
|
|
37
|
+
return self.thread_context.pop(key)
|
|
38
|
+
|
|
39
|
+
def get_all(self):
|
|
40
|
+
return dict(self.thread_context.items())
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
|
|
3
|
+
from testit_python_commons.services.plugin_manager import TmsPluginManager
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def adapter_logger(function):
|
|
7
|
+
@wraps(function)
|
|
8
|
+
def wrapper(*args, **kwargs):
|
|
9
|
+
from testit_python_commons.services.utils import Utils
|
|
10
|
+
|
|
11
|
+
logger = TmsPluginManager.get_logger()
|
|
12
|
+
|
|
13
|
+
parameters = Utils.get_function_parameters(function, *args, **kwargs)
|
|
14
|
+
|
|
15
|
+
message = f'Method "{function.__name__}" started'
|
|
16
|
+
|
|
17
|
+
if parameters:
|
|
18
|
+
message += f' with parameters: {parameters}'
|
|
19
|
+
|
|
20
|
+
logger.debug(message)
|
|
21
|
+
|
|
22
|
+
result = function(*args, **kwargs)
|
|
23
|
+
|
|
24
|
+
message = f'Method "{function.__name__}" finished'
|
|
25
|
+
|
|
26
|
+
if result is not None:
|
|
27
|
+
message += f' with result: {result}'
|
|
28
|
+
|
|
29
|
+
logger.debug(message)
|
|
30
|
+
|
|
31
|
+
return result
|
|
32
|
+
return wrapper
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from pluggy import PluginManager
|
|
4
|
+
|
|
5
|
+
from testit_python_commons.app_properties import AppProperties
|
|
6
|
+
from testit_python_commons.services.step_manager import StepManager
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TmsPluginManager:
|
|
10
|
+
__plugin_manager = None
|
|
11
|
+
__adapter_manager = None
|
|
12
|
+
__fixture_manager = None
|
|
13
|
+
__step_manager = None
|
|
14
|
+
__logger = None
|
|
15
|
+
# None: non-pytest or undecided; False: pytest without --testit; True: pytest with --testit
|
|
16
|
+
__pytest_tms_report = None
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
def set_pytest_tms_report(cls, enabled):
|
|
20
|
+
cls.__pytest_tms_report = enabled
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def get_plugin_manager(cls):
|
|
24
|
+
if cls.__plugin_manager is None:
|
|
25
|
+
cls.__plugin_manager = PluginManager("testit")
|
|
26
|
+
|
|
27
|
+
return cls.__plugin_manager
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def get_adapter_manager(cls, option=None):
|
|
31
|
+
if cls.__pytest_tms_report is False:
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
if cls.__adapter_manager is None:
|
|
35
|
+
from testit_python_commons.client.client_configuration import (
|
|
36
|
+
ClientConfiguration,
|
|
37
|
+
)
|
|
38
|
+
from testit_python_commons.services.adapter_manager import AdapterManager
|
|
39
|
+
from testit_python_commons.services.adapter_manager_configuration import (
|
|
40
|
+
AdapterManagerConfiguration,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
app_properties = AppProperties.load_properties(option)
|
|
44
|
+
|
|
45
|
+
cls.get_logger(app_properties.get("logs") == "true")
|
|
46
|
+
|
|
47
|
+
client_configuration = ClientConfiguration(app_properties)
|
|
48
|
+
adapter_configuration = AdapterManagerConfiguration(app_properties)
|
|
49
|
+
fixture_manager = cls.get_fixture_manager()
|
|
50
|
+
|
|
51
|
+
cls.__adapter_manager = AdapterManager(
|
|
52
|
+
adapter_configuration, client_configuration, fixture_manager
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return cls.__adapter_manager
|
|
56
|
+
|
|
57
|
+
@classmethod
|
|
58
|
+
def get_fixture_manager(cls):
|
|
59
|
+
if cls.__fixture_manager is None:
|
|
60
|
+
from testit_python_commons.services.fixture_manager import FixtureManager
|
|
61
|
+
|
|
62
|
+
cls.__fixture_manager = FixtureManager()
|
|
63
|
+
|
|
64
|
+
return cls.__fixture_manager
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
def get_step_manager(cls) -> StepManager:
|
|
68
|
+
if cls.__step_manager is None:
|
|
69
|
+
cls.__step_manager = StepManager()
|
|
70
|
+
|
|
71
|
+
return cls.__step_manager
|
|
72
|
+
|
|
73
|
+
@classmethod
|
|
74
|
+
def get_logger(cls, debug: bool = False):
|
|
75
|
+
if cls.__logger is None:
|
|
76
|
+
# Fix for live log duplication issue
|
|
77
|
+
# Create a dedicated handler for TmsLogger to avoid affecting global logging configuration
|
|
78
|
+
cls.__logger = logging.getLogger("TmsLogger")
|
|
79
|
+
|
|
80
|
+
# Only add handler if none exists to prevent duplicates
|
|
81
|
+
if not cls.__logger.handlers:
|
|
82
|
+
handler = logging.StreamHandler()
|
|
83
|
+
formatter = logging.Formatter(
|
|
84
|
+
"\n%(levelname)s (%(asctime)s): %(message)s"
|
|
85
|
+
)
|
|
86
|
+
handler.setFormatter(formatter)
|
|
87
|
+
cls.__logger.addHandler(handler)
|
|
88
|
+
|
|
89
|
+
# Always show WARNING and above (WARNING, ERROR, CRITICAL)
|
|
90
|
+
# Only show DEBUG and INFO when debug mode is enabled
|
|
91
|
+
level = logging.DEBUG if debug else logging.WARNING
|
|
92
|
+
cls.__logger.setLevel(level)
|
|
93
|
+
|
|
94
|
+
# Prevent propagation to root logger to avoid interference with user loggers
|
|
95
|
+
cls.__logger.propagate = False
|
|
96
|
+
|
|
97
|
+
return cls.__logger
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def __getattr__(cls, attribute):
|
|
101
|
+
return getattr(cls.get_plugin_manager(), attribute)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import random
|
|
3
|
+
import time
|
|
4
|
+
from http.client import RemoteDisconnected
|
|
5
|
+
|
|
6
|
+
import testit_api_client
|
|
7
|
+
import urllib3
|
|
8
|
+
|
|
9
|
+
CONNECTION_RETRIES = 3
|
|
10
|
+
CONNECTION_RETRY_DELAY_SEC = 1
|
|
11
|
+
API_EXCEPTION_RETRIES = 10
|
|
12
|
+
NON_RETRIABLE_API_STATUS_CODES = (400, 404)
|
|
13
|
+
|
|
14
|
+
_RETRIABLE_CONNECTION_TYPES = (
|
|
15
|
+
urllib3.exceptions.ProtocolError,
|
|
16
|
+
urllib3.exceptions.NewConnectionError,
|
|
17
|
+
ConnectionError,
|
|
18
|
+
ConnectionResetError,
|
|
19
|
+
ConnectionAbortedError,
|
|
20
|
+
RemoteDisconnected,
|
|
21
|
+
TimeoutError,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def is_non_retriable_api_exception(exc: BaseException) -> bool:
|
|
26
|
+
return (
|
|
27
|
+
isinstance(exc, testit_api_client.exceptions.ApiException)
|
|
28
|
+
and int(exc.status) in NON_RETRIABLE_API_STATUS_CODES
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def is_retriable_connection_error(exc: BaseException) -> bool:
|
|
33
|
+
seen = set()
|
|
34
|
+
current = exc
|
|
35
|
+
while current is not None and id(current) not in seen:
|
|
36
|
+
seen.add(id(current))
|
|
37
|
+
if isinstance(current, _RETRIABLE_CONNECTION_TYPES):
|
|
38
|
+
return True
|
|
39
|
+
current = current.__cause__ or current.__context__
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _execute_with_connection_retries(func, args, kwargs):
|
|
44
|
+
connection_attempts = 0
|
|
45
|
+
|
|
46
|
+
while True:
|
|
47
|
+
try:
|
|
48
|
+
return func(*args, **kwargs)
|
|
49
|
+
except BaseException as e:
|
|
50
|
+
if not is_retriable_connection_error(e):
|
|
51
|
+
raise
|
|
52
|
+
|
|
53
|
+
connection_attempts += 1
|
|
54
|
+
logging.warning(
|
|
55
|
+
'Connection error in %s (attempt %d/%d): %s',
|
|
56
|
+
func.__name__,
|
|
57
|
+
connection_attempts,
|
|
58
|
+
CONNECTION_RETRIES,
|
|
59
|
+
e,
|
|
60
|
+
)
|
|
61
|
+
if connection_attempts > CONNECTION_RETRIES:
|
|
62
|
+
raise
|
|
63
|
+
|
|
64
|
+
time.sleep(CONNECTION_RETRY_DELAY_SEC)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def retry_on_connection_error(func):
|
|
68
|
+
def retry_wrapper(*args, **kwargs):
|
|
69
|
+
return _execute_with_connection_retries(func, args, kwargs)
|
|
70
|
+
|
|
71
|
+
return retry_wrapper
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def retry(func):
|
|
75
|
+
def retry_wrapper(*args, **kwargs):
|
|
76
|
+
api_attempts = 0
|
|
77
|
+
|
|
78
|
+
while True:
|
|
79
|
+
try:
|
|
80
|
+
return _execute_with_connection_retries(func, args, kwargs)
|
|
81
|
+
except testit_api_client.exceptions.ApiException as e:
|
|
82
|
+
if is_non_retriable_api_exception(e):
|
|
83
|
+
raise
|
|
84
|
+
|
|
85
|
+
api_attempts += 1
|
|
86
|
+
logging.error(e)
|
|
87
|
+
if api_attempts >= API_EXCEPTION_RETRIES:
|
|
88
|
+
raise
|
|
89
|
+
|
|
90
|
+
time.sleep(random.randrange(0, 100) / 100)
|
|
91
|
+
|
|
92
|
+
return retry_wrapper
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from testit_python_commons.models.step_result import StepResult
|
|
4
|
+
from testit_python_commons.services.step_result_storage import StepResultStorage
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class StepManager:
|
|
8
|
+
__steps_tree: List[StepResult] = []
|
|
9
|
+
|
|
10
|
+
def __init__(self):
|
|
11
|
+
self.__storage = StepResultStorage()
|
|
12
|
+
|
|
13
|
+
def start_step(self, step: StepResult):
|
|
14
|
+
if self.__storage.get_count():
|
|
15
|
+
parent_step: StepResult = self.__storage.get_last()
|
|
16
|
+
|
|
17
|
+
step_results_from_parent_step = parent_step.get_step_results()
|
|
18
|
+
step_results_from_parent_step.append(step)
|
|
19
|
+
parent_step.set_step_results(step_results_from_parent_step)
|
|
20
|
+
|
|
21
|
+
self.__storage.add(step)
|
|
22
|
+
|
|
23
|
+
def stop_step(self):
|
|
24
|
+
if self.__storage.get_count() == 1:
|
|
25
|
+
self.__steps_tree.append(self.__storage.get_last())
|
|
26
|
+
|
|
27
|
+
self.__storage.remove_last()
|
|
28
|
+
|
|
29
|
+
def get_active_step(self) -> StepResult:
|
|
30
|
+
return self.__storage.get_last()
|
|
31
|
+
|
|
32
|
+
def get_steps_tree(self) -> List[StepResult]:
|
|
33
|
+
steps_tree = self.__steps_tree.copy()
|
|
34
|
+
self.__steps_tree.clear()
|
|
35
|
+
|
|
36
|
+
return steps_tree
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
from testit_python_commons.models.step_result import StepResult
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class StepResultStorage:
|
|
8
|
+
__storage: List[StepResult] = []
|
|
9
|
+
|
|
10
|
+
def add(self, step_result: StepResult):
|
|
11
|
+
self.__storage.append(step_result)
|
|
12
|
+
|
|
13
|
+
def get_last(self):
|
|
14
|
+
if not self.__storage:
|
|
15
|
+
return
|
|
16
|
+
|
|
17
|
+
return self.__storage[-1]
|
|
18
|
+
|
|
19
|
+
def remove_last(self):
|
|
20
|
+
try:
|
|
21
|
+
self.__storage.pop()
|
|
22
|
+
except Exception as exc:
|
|
23
|
+
logging.error(f'Cannot remove last step from storage. Storage is empty. {exc}')
|
|
24
|
+
|
|
25
|
+
def get_count(self) -> int:
|
|
26
|
+
return len(self.__storage)
|