baldertest 0.1.0__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.
- _balder/__init__.py +12 -0
- _balder/_version.py +34 -0
- _balder/balder_plugin.py +73 -0
- _balder/balder_session.py +341 -0
- _balder/balder_settings.py +15 -0
- _balder/cnnrelations/__init__.py +7 -0
- _balder/cnnrelations/and_connection_relation.py +176 -0
- _balder/cnnrelations/base_connection_relation.py +270 -0
- _balder/cnnrelations/or_connection_relation.py +65 -0
- _balder/collector.py +874 -0
- _balder/connection.py +863 -0
- _balder/connection_metadata.py +255 -0
- _balder/console/__init__.py +0 -0
- _balder/console/balder.py +58 -0
- _balder/controllers/__init__.py +12 -0
- _balder/controllers/base_device_controller.py +72 -0
- _balder/controllers/controller.py +29 -0
- _balder/controllers/device_controller.py +446 -0
- _balder/controllers/feature_controller.py +715 -0
- _balder/controllers/normal_scenario_setup_controller.py +402 -0
- _balder/controllers/scenario_controller.py +524 -0
- _balder/controllers/setup_controller.py +134 -0
- _balder/controllers/vdevice_controller.py +95 -0
- _balder/decorator_connect.py +104 -0
- _balder/decorator_covered_by.py +74 -0
- _balder/decorator_fixture.py +29 -0
- _balder/decorator_for_vdevice.py +118 -0
- _balder/decorator_gateway.py +34 -0
- _balder/decorator_insert_into_tree.py +52 -0
- _balder/decorator_parametrize.py +31 -0
- _balder/decorator_parametrize_by_feature.py +36 -0
- _balder/device.py +18 -0
- _balder/exceptions.py +182 -0
- _balder/executor/__init__.py +0 -0
- _balder/executor/basic_executable_executor.py +133 -0
- _balder/executor/basic_executor.py +205 -0
- _balder/executor/executor_tree.py +217 -0
- _balder/executor/parametrized_testcase_executor.py +52 -0
- _balder/executor/scenario_executor.py +169 -0
- _balder/executor/setup_executor.py +163 -0
- _balder/executor/testcase_executor.py +203 -0
- _balder/executor/unresolved_parametrized_testcase_executor.py +184 -0
- _balder/executor/variation_executor.py +882 -0
- _balder/exit_code.py +19 -0
- _balder/feature.py +74 -0
- _balder/feature_replacement_mapping.py +107 -0
- _balder/feature_vdevice_mapping.py +88 -0
- _balder/fixture_definition_scope.py +19 -0
- _balder/fixture_execution_level.py +22 -0
- _balder/fixture_manager.py +483 -0
- _balder/fixture_metadata.py +26 -0
- _balder/node_gateway.py +103 -0
- _balder/objects/__init__.py +0 -0
- _balder/objects/connections/__init__.py +0 -0
- _balder/objects/connections/osi_1_physical.py +116 -0
- _balder/objects/connections/osi_2_datalink.py +35 -0
- _balder/objects/connections/osi_3_network.py +47 -0
- _balder/objects/connections/osi_4_transport.py +40 -0
- _balder/objects/connections/osi_5_session.py +13 -0
- _balder/objects/connections/osi_6_presentation.py +13 -0
- _balder/objects/connections/osi_7_application.py +83 -0
- _balder/objects/devices/__init__.py +0 -0
- _balder/objects/devices/this_device.py +12 -0
- _balder/parametrization.py +75 -0
- _balder/plugin_manager.py +138 -0
- _balder/previous_executor_mark.py +23 -0
- _balder/routing_path.py +335 -0
- _balder/scenario.py +20 -0
- _balder/setup.py +18 -0
- _balder/solver.py +246 -0
- _balder/testresult.py +163 -0
- _balder/unmapped_vdevice.py +13 -0
- _balder/utils/__init__.py +0 -0
- _balder/utils/functions.py +103 -0
- _balder/utils/inner_device_managing_metaclass.py +14 -0
- _balder/utils/mixin_can_be_covered_by_executor.py +24 -0
- _balder/utils/typings.py +4 -0
- _balder/vdevice.py +9 -0
- balder/__init__.py +56 -0
- balder/connections.py +43 -0
- balder/devices.py +9 -0
- balder/exceptions.py +44 -0
- balder/parametrization.py +8 -0
- baldertest-0.1.0.dist-info/METADATA +356 -0
- baldertest-0.1.0.dist-info/RECORD +89 -0
- baldertest-0.1.0.dist-info/WHEEL +5 -0
- baldertest-0.1.0.dist-info/entry_points.txt +2 -0
- baldertest-0.1.0.dist-info/licenses/LICENSE +21 -0
- baldertest-0.1.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Union, List, Type, TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
from dataclasses import fields
|
|
5
|
+
from _balder.executor.setup_executor import SetupExecutor
|
|
6
|
+
from _balder.executor.basic_executable_executor import BasicExecutableExecutor
|
|
7
|
+
from _balder.fixture_execution_level import FixtureExecutionLevel
|
|
8
|
+
from _balder.testresult import ResultState, BranchBodyResult, ResultSummary
|
|
9
|
+
from _balder.previous_executor_mark import PreviousExecutorMark
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from _balder.setup import Setup
|
|
13
|
+
from _balder.fixture_manager import FixtureManager
|
|
14
|
+
from _balder.executor.scenario_executor import ScenarioExecutor
|
|
15
|
+
from _balder.executor.variation_executor import VariationExecutor
|
|
16
|
+
from _balder.executor.testcase_executor import TestcaseExecutor
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ExecutorTree(BasicExecutableExecutor):
|
|
20
|
+
"""
|
|
21
|
+
This class is the root object of the executor tree structure
|
|
22
|
+
"""
|
|
23
|
+
fixture_execution_level = FixtureExecutionLevel.SESSION
|
|
24
|
+
LINE_LENGTH = 120
|
|
25
|
+
|
|
26
|
+
def __init__(self, fixture_manager: FixtureManager):
|
|
27
|
+
super().__init__()
|
|
28
|
+
self._setup_executors: List[SetupExecutor] = []
|
|
29
|
+
self._fixture_manager = fixture_manager
|
|
30
|
+
|
|
31
|
+
# contains the result object for the BODY part of this branch (will be overwritten in :class:`TestcaseExecutor`)
|
|
32
|
+
self.body_result = BranchBodyResult(self)
|
|
33
|
+
|
|
34
|
+
# ---------------------------------- STATIC METHODS ----------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
# ---------------------------------- CLASS METHODS ----------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
# ---------------------------------- PROPERTIES --------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def all_child_executors(self) -> List[BasicExecutableExecutor] | None:
|
|
42
|
+
return self._setup_executors
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def base_instance(self) -> object:
|
|
46
|
+
"""returns None because this element is a ExecutorTree"""
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def fixture_manager(self) -> FixtureManager:
|
|
51
|
+
"""returns the fixture manager of this tree"""
|
|
52
|
+
return self._fixture_manager
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def parent_executor(self) -> None:
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
# ---------------------------------- PROTECTED METHODS -------------------------------------------------------------
|
|
59
|
+
|
|
60
|
+
def _prepare_execution(self, show_discarded):
|
|
61
|
+
if not show_discarded:
|
|
62
|
+
self.update_inner_feature_reference_in_all_setups()
|
|
63
|
+
|
|
64
|
+
def _body_execution(self, show_discarded):
|
|
65
|
+
for cur_setup_executor in self.get_setup_executors():
|
|
66
|
+
prev_mark = cur_setup_executor.prev_mark
|
|
67
|
+
if cur_setup_executor.has_runnable_tests(show_discarded) or cur_setup_executor.has_skipped_tests():
|
|
68
|
+
cur_setup_executor.execute(show_discarded=show_discarded)
|
|
69
|
+
elif prev_mark == PreviousExecutorMark.SKIP:
|
|
70
|
+
cur_setup_executor.set_result_for_whole_branch(ResultState.SKIP)
|
|
71
|
+
elif prev_mark == PreviousExecutorMark.COVERED_BY:
|
|
72
|
+
cur_setup_executor.set_result_for_whole_branch(ResultState.COVERED_BY)
|
|
73
|
+
else:
|
|
74
|
+
cur_setup_executor.set_result_for_whole_branch(ResultState.NOT_RUN)
|
|
75
|
+
|
|
76
|
+
def _cleanup_execution(self, show_discarded):
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
# ---------------------------------- METHODS -----------------------------------------------------------------------
|
|
80
|
+
|
|
81
|
+
def get_setup_executors(self) -> List[SetupExecutor]:
|
|
82
|
+
"""returns all setup executors of this tree"""
|
|
83
|
+
return self._setup_executors
|
|
84
|
+
|
|
85
|
+
def get_all_scenario_executors(self) -> List[ScenarioExecutor]:
|
|
86
|
+
"""
|
|
87
|
+
returns a list with all scenario executors
|
|
88
|
+
"""
|
|
89
|
+
all_scenario_executor = []
|
|
90
|
+
for cur_setup_executor in self.get_setup_executors():
|
|
91
|
+
all_scenario_executor += cur_setup_executor.get_scenario_executors()
|
|
92
|
+
return all_scenario_executor
|
|
93
|
+
|
|
94
|
+
def get_all_variation_executors(self, return_discarded=False) -> List[VariationExecutor]:
|
|
95
|
+
"""
|
|
96
|
+
returns a list with all variation executors
|
|
97
|
+
"""
|
|
98
|
+
all_variation_executor = []
|
|
99
|
+
for cur_scenario_executor in self.get_all_scenario_executors():
|
|
100
|
+
all_variation_executor += cur_scenario_executor.get_variation_executors(return_discarded=return_discarded)
|
|
101
|
+
return all_variation_executor
|
|
102
|
+
|
|
103
|
+
def get_all_testcase_executors(self) -> List[TestcaseExecutor]:
|
|
104
|
+
"""
|
|
105
|
+
returns a list with all testcase executors
|
|
106
|
+
"""
|
|
107
|
+
all_testcase_executor = []
|
|
108
|
+
for cur_scenario_executor in self.get_all_scenario_executors():
|
|
109
|
+
for cur_variation_executor in cur_scenario_executor.get_variation_executors():
|
|
110
|
+
all_testcase_executor += cur_variation_executor.get_testcase_executors()
|
|
111
|
+
return all_testcase_executor
|
|
112
|
+
|
|
113
|
+
def add_setup_executor(self, setup_executor: SetupExecutor):
|
|
114
|
+
"""
|
|
115
|
+
This method adds a new :class:`SetupExecutor` to the child element's list of this tree object
|
|
116
|
+
"""
|
|
117
|
+
if not isinstance(setup_executor, SetupExecutor):
|
|
118
|
+
raise TypeError("the given object `setup_executor` must be of type `SetupExecutor`")
|
|
119
|
+
if setup_executor in self._setup_executors:
|
|
120
|
+
raise ValueError("the given object `setup_executor` already exists in child list")
|
|
121
|
+
self._setup_executors.append(setup_executor)
|
|
122
|
+
|
|
123
|
+
def get_executor_for_setup(self, setup: Type[Setup]) -> Union[SetupExecutor, None]:
|
|
124
|
+
"""
|
|
125
|
+
This method searches for a SetupExecutor in the internal list for the given :class:`Setup` type
|
|
126
|
+
|
|
127
|
+
:param setup: the setup class for which the executor is being searched for
|
|
128
|
+
|
|
129
|
+
:return: returns the associated SetupExecutor or None if there are no matches for the given type
|
|
130
|
+
"""
|
|
131
|
+
for cur_setup_executor in self._setup_executors:
|
|
132
|
+
if cur_setup_executor.base_setup_class.__class__ == setup:
|
|
133
|
+
return cur_setup_executor
|
|
134
|
+
# can not find some
|
|
135
|
+
return None
|
|
136
|
+
|
|
137
|
+
def cleanup_empty_executor_branches(self, consider_discarded=False):
|
|
138
|
+
to_remove_executor = []
|
|
139
|
+
for cur_setup_executor in self.get_setup_executors():
|
|
140
|
+
cur_setup_executor.cleanup_empty_executor_branches(consider_discarded=consider_discarded)
|
|
141
|
+
if len(cur_setup_executor.get_scenario_executors()) == 0:
|
|
142
|
+
# remove this whole executor because it has no children anymore
|
|
143
|
+
to_remove_executor.append(cur_setup_executor)
|
|
144
|
+
for cur_setup_executor in to_remove_executor:
|
|
145
|
+
self._setup_executors.remove(cur_setup_executor)
|
|
146
|
+
|
|
147
|
+
def update_inner_feature_reference_in_all_setups(self):
|
|
148
|
+
"""
|
|
149
|
+
This method iterates over all setups and triggers the method that updates the inner feature references.
|
|
150
|
+
|
|
151
|
+
This needs to be done on ExecutorTree level, because this reference should also be accessible within session
|
|
152
|
+
fixtures (in case they have setup scope).
|
|
153
|
+
"""
|
|
154
|
+
for cur_setup_executor in self.get_setup_executors():
|
|
155
|
+
cur_setup_executor.update_inner_referenced_feature_instances()
|
|
156
|
+
|
|
157
|
+
def execute(self, show_discarded=False) -> None:
|
|
158
|
+
"""
|
|
159
|
+
This method executes this branch of the tree
|
|
160
|
+
"""
|
|
161
|
+
start_text = "START TESTSESSION"
|
|
162
|
+
end_text = "FINISH TESTSESSION"
|
|
163
|
+
|
|
164
|
+
def print_line(text):
|
|
165
|
+
full_text = int((self.LINE_LENGTH - (len(start_text) + 2)) / 2) * "=" + " " + text + " "
|
|
166
|
+
full_text += "=" * (self.LINE_LENGTH - len(full_text))
|
|
167
|
+
print(full_text)
|
|
168
|
+
|
|
169
|
+
print_line(start_text)
|
|
170
|
+
# check if there exists runnable elements
|
|
171
|
+
runnables = [cur_exec.has_runnable_tests(consider_discarded_too=show_discarded)
|
|
172
|
+
for cur_exec in self.get_setup_executors()]
|
|
173
|
+
one_or_more_runnable_setups = None if len(runnables) == 0 else max(runnables)
|
|
174
|
+
if one_or_more_runnable_setups:
|
|
175
|
+
super().execute(show_discarded=show_discarded)
|
|
176
|
+
else:
|
|
177
|
+
print("NO EXECUTABLE SETUPS/SCENARIOS FOUND")
|
|
178
|
+
print_line(end_text)
|
|
179
|
+
summary = self.testsummary()
|
|
180
|
+
is_first = True
|
|
181
|
+
for cur_field in fields(ResultSummary):
|
|
182
|
+
if is_first:
|
|
183
|
+
is_first = False
|
|
184
|
+
else:
|
|
185
|
+
print(" | ", end="")
|
|
186
|
+
print(f"TOTAL {cur_field.name.upper()}: {getattr(summary, cur_field.name)}", end="")
|
|
187
|
+
print("")
|
|
188
|
+
|
|
189
|
+
def print_tree(self, show_discarded=False) -> None:
|
|
190
|
+
"""this method is an auxiliary method which outputs the entire tree"""
|
|
191
|
+
print("RESOLVING OVERVIEW", end="\n\n")
|
|
192
|
+
for cur_setup_executor in self.get_setup_executors():
|
|
193
|
+
for cur_scenario_executor in cur_setup_executor.get_scenario_executors():
|
|
194
|
+
for cur_variation_executor in cur_scenario_executor.get_variation_executors(
|
|
195
|
+
return_discarded=show_discarded):
|
|
196
|
+
applicable = cur_variation_executor.prev_mark != PreviousExecutorMark.DISCARDED
|
|
197
|
+
start_char = '+' if applicable else 'X'
|
|
198
|
+
print(start_char * self.LINE_LENGTH)
|
|
199
|
+
applicability_str = "[APPLICABLE]" if applicable else "[DISCARDED] "
|
|
200
|
+
print(f"{start_char} {applicability_str} Scenario "
|
|
201
|
+
f"`{cur_scenario_executor.base_scenario_class.__class__.__qualname__}` <-> "
|
|
202
|
+
f"Setup `{cur_setup_executor.base_setup_class.__class__.__qualname__}`")
|
|
203
|
+
mapping_printings = {}
|
|
204
|
+
for cur_scenario_device, cur_setup_device in cur_variation_executor.base_device_mapping.items():
|
|
205
|
+
mapping_printings[f" {cur_scenario_device.__qualname__}"] = str(cur_setup_device.__qualname__)
|
|
206
|
+
max_len = max(len(cur_elem) for cur_elem in mapping_printings.keys())
|
|
207
|
+
for cur_key, cur_val in mapping_printings.items():
|
|
208
|
+
print(("{} {:<" + str(max_len) + "} = {}").format(start_char, cur_key, cur_val))
|
|
209
|
+
for cur_testcase_excutor in cur_variation_executor.get_testcase_executors():
|
|
210
|
+
print(f"{start_char} -> Testcase<"
|
|
211
|
+
f"{cur_testcase_excutor.base_testcase_callable.__qualname__}>")
|
|
212
|
+
if cur_variation_executor.prev_mark == PreviousExecutorMark.DISCARDED:
|
|
213
|
+
print(f"{start_char}")
|
|
214
|
+
print(f"{start_char} DISCARDED BECAUSE "
|
|
215
|
+
f"`{cur_variation_executor.not_applicable_variation_exc.args[0]}`")
|
|
216
|
+
print(('+' if applicable else 'X') * self.LINE_LENGTH)
|
|
217
|
+
print('')
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Any, TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
from collections import OrderedDict
|
|
5
|
+
from _balder.utils.functions import get_method_type
|
|
6
|
+
from .testcase_executor import TestcaseExecutor
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from _balder.executor.unresolved_parametrized_testcase_executor import UnresolvedParametrizedTestcaseExecutor
|
|
10
|
+
from _balder.executor.variation_executor import VariationExecutor
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ParametrizedTestcaseExecutor(TestcaseExecutor):
|
|
14
|
+
"""
|
|
15
|
+
special testcase executor for parametrized testcases
|
|
16
|
+
"""
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
testcase: callable,
|
|
20
|
+
parent: VariationExecutor,
|
|
21
|
+
parametrization: OrderedDict[str, Any] = None,
|
|
22
|
+
unresolved_group_obj: UnresolvedParametrizedTestcaseExecutor = None
|
|
23
|
+
) -> None:
|
|
24
|
+
super().__init__(testcase=testcase, parent=parent)
|
|
25
|
+
self._parametrization = parametrization
|
|
26
|
+
# holds a reference to the parent unresolved object (if it has dynamic parametrized components
|
|
27
|
+
self._unresolved_group_obj: UnresolvedParametrizedTestcaseExecutor | None = unresolved_group_obj
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def full_test_name_str(self) -> str:
|
|
31
|
+
"""
|
|
32
|
+
returns the name of the test method, that should be used in output
|
|
33
|
+
"""
|
|
34
|
+
# try to get string name for parametrization
|
|
35
|
+
if self._parametrization:
|
|
36
|
+
# todo what if it is not convertable to string?
|
|
37
|
+
parametrization_name = ';'.join([str(e) for e in self._parametrization.values()])
|
|
38
|
+
return f"{super().full_test_name_str}[{parametrization_name}]"
|
|
39
|
+
return super().full_test_name_str
|
|
40
|
+
|
|
41
|
+
def get_all_test_method_args(self):
|
|
42
|
+
func_type = get_method_type(self.base_testcase_obj.__class__, self.base_testcase_callable)
|
|
43
|
+
all_kwargs = self.fixture_manager.get_all_attribute_values(
|
|
44
|
+
self,
|
|
45
|
+
self.base_testcase_obj.__class__,
|
|
46
|
+
self.base_testcase_callable,
|
|
47
|
+
func_type,
|
|
48
|
+
ignore_attributes=self._parametrization.keys()
|
|
49
|
+
)
|
|
50
|
+
if self._parametrization:
|
|
51
|
+
all_kwargs.update(self._parametrization)
|
|
52
|
+
return all_kwargs
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Type, Union, List, TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
from _balder.fixture_execution_level import FixtureExecutionLevel
|
|
5
|
+
from _balder.testresult import ResultState, BranchBodyResult
|
|
6
|
+
from _balder.executor.basic_executable_executor import BasicExecutableExecutor
|
|
7
|
+
from _balder.executor.variation_executor import VariationExecutor
|
|
8
|
+
from _balder.previous_executor_mark import PreviousExecutorMark
|
|
9
|
+
from _balder.controllers.scenario_controller import ScenarioController
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from _balder.scenario import Scenario
|
|
13
|
+
from _balder.executor.setup_executor import SetupExecutor
|
|
14
|
+
from _balder.fixture_manager import FixtureManager
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ScenarioExecutor(BasicExecutableExecutor):
|
|
18
|
+
"""
|
|
19
|
+
A ScenarioExecutor can contain :meth:`VariationExecutor` as children.
|
|
20
|
+
"""
|
|
21
|
+
fixture_execution_level = FixtureExecutionLevel.SCENARIO
|
|
22
|
+
|
|
23
|
+
def __init__(self, scenario: Type[Scenario], parent: SetupExecutor):
|
|
24
|
+
super().__init__()
|
|
25
|
+
self._variation_executors: List[VariationExecutor] = []
|
|
26
|
+
# check if instance already exists
|
|
27
|
+
if hasattr(scenario, "_instance") and scenario._instance is not None and \
|
|
28
|
+
isinstance(scenario._instance, scenario):
|
|
29
|
+
self._base_scenario_class = scenario._instance
|
|
30
|
+
else:
|
|
31
|
+
self._base_scenario_class = scenario()
|
|
32
|
+
scenario._instance = self._base_scenario_class
|
|
33
|
+
self._parent_executor = parent
|
|
34
|
+
self._fixture_manager = parent.fixture_manager
|
|
35
|
+
|
|
36
|
+
# contains the result object for the BODY part of this branch
|
|
37
|
+
self.body_result = BranchBodyResult(self)
|
|
38
|
+
|
|
39
|
+
# if the related scenario class of this executor is decorated with ``@covered_by``, this property contains a
|
|
40
|
+
# list with the :class:`ScenarioExecutor` or/and :class:`TestcaseExecutor` that covers this one
|
|
41
|
+
self.covered_by_executors = None
|
|
42
|
+
|
|
43
|
+
# ---------------------------------- STATIC METHODS ----------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
# ---------------------------------- CLASS METHODS ----------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
# ---------------------------------- PROPERTIES --------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def all_child_executors(self) -> List[VariationExecutor]:
|
|
51
|
+
return self._variation_executors
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def parent_executor(self) -> SetupExecutor:
|
|
55
|
+
return self._parent_executor
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def base_instance(self) -> object:
|
|
59
|
+
"""
|
|
60
|
+
returns the base class instance to which this executor instance belongs
|
|
61
|
+
"""
|
|
62
|
+
return self.base_scenario_class
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def base_scenario_class(self) -> Scenario:
|
|
66
|
+
"""returns the :class:`Scenario` class that belongs to this executor"""
|
|
67
|
+
return self._base_scenario_class
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def base_scenario_controller(self) -> ScenarioController:
|
|
71
|
+
"""returns the :class:`ScenarioController` for the setup object of this executor"""
|
|
72
|
+
return ScenarioController.get_for(self.base_scenario_class.__class__)
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def fixture_manager(self) -> FixtureManager:
|
|
76
|
+
"""returns the current active fixture manager that belongs to this scenario executor"""
|
|
77
|
+
return self._fixture_manager
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def all_run_tests(self) -> List[callable]:
|
|
81
|
+
"""returns a list of all test methods that are declared to `RUN` in their base :class:`Scenario` class"""
|
|
82
|
+
return self.base_scenario_controller.get_run_test_methods()
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def all_skip_tests(self) -> List[callable]:
|
|
86
|
+
"""returns a list of all test methods that are declared to `SKIP` in their base :class:`Scenario` class"""
|
|
87
|
+
return self.base_scenario_controller.get_skip_test_methods()
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def all_ignore_tests(self) -> List[callable]:
|
|
91
|
+
"""returns a list of all test methods that are declared to `IGNORE` in their base :class:`Scenario` class"""
|
|
92
|
+
return self.base_scenario_controller.get_ignore_test_methods()
|
|
93
|
+
|
|
94
|
+
# ---------------------------------- PROTECTED METHODS -------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
def _prepare_execution(self, show_discarded):
|
|
97
|
+
print(f" SCENARIO {self.base_scenario_class.__class__.__name__}")
|
|
98
|
+
|
|
99
|
+
def _body_execution(self, show_discarded):
|
|
100
|
+
for cur_variation_executor in self.get_variation_executors(return_discarded=show_discarded):
|
|
101
|
+
prev_mark = cur_variation_executor.prev_mark
|
|
102
|
+
if cur_variation_executor.has_runnable_tests() or cur_variation_executor.has_skipped_tests():
|
|
103
|
+
cur_variation_executor.execute(show_discarded=show_discarded)
|
|
104
|
+
elif prev_mark == PreviousExecutorMark.SKIP:
|
|
105
|
+
cur_variation_executor.set_result_for_whole_branch(ResultState.SKIP)
|
|
106
|
+
elif prev_mark == PreviousExecutorMark.COVERED_BY:
|
|
107
|
+
cur_variation_executor.set_result_for_whole_branch(ResultState.COVERED_BY)
|
|
108
|
+
else:
|
|
109
|
+
cur_variation_executor.set_result_for_whole_branch(ResultState.NOT_RUN)
|
|
110
|
+
|
|
111
|
+
def _cleanup_execution(self, show_discarded):
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
# ---------------------------------- METHODS -----------------------------------------------------------------------
|
|
115
|
+
|
|
116
|
+
def get_variation_executors(self, return_discarded=False) -> List[VariationExecutor]:
|
|
117
|
+
"""
|
|
118
|
+
:param return_discarded: True if the method should return discarded variations too
|
|
119
|
+
|
|
120
|
+
:return: returns all variation executors that are child executor of this scenario executor
|
|
121
|
+
"""
|
|
122
|
+
if not return_discarded:
|
|
123
|
+
return [cur_executor for cur_executor in self._variation_executors
|
|
124
|
+
if cur_executor.prev_mark != PreviousExecutorMark.DISCARDED]
|
|
125
|
+
return self._variation_executors
|
|
126
|
+
|
|
127
|
+
def cleanup_empty_executor_branches(self, consider_discarded=False):
|
|
128
|
+
"""
|
|
129
|
+
This method removes all sub executors that are empty and not relevant anymore.
|
|
130
|
+
"""
|
|
131
|
+
to_remove_executor = []
|
|
132
|
+
for cur_variation_executor in self.get_variation_executors(return_discarded=consider_discarded):
|
|
133
|
+
if len(cur_variation_executor.get_testcase_executors()) == 0:
|
|
134
|
+
# remove this whole executor because it has no children anymore
|
|
135
|
+
to_remove_executor.append(cur_variation_executor)
|
|
136
|
+
for cur_variation_executor in to_remove_executor:
|
|
137
|
+
self._variation_executors.remove(cur_variation_executor)
|
|
138
|
+
|
|
139
|
+
def get_covered_by_element(self) -> List[Union[Scenario, callable]]:
|
|
140
|
+
"""
|
|
141
|
+
This method returns a list of elements where the whole scenario is covered from. This means, that the whole
|
|
142
|
+
test methods in this scenario are already be covered from one of the elements in the list.
|
|
143
|
+
"""
|
|
144
|
+
return self.base_scenario_controller.get_abs_covered_by_dict().get(None, [])
|
|
145
|
+
|
|
146
|
+
def add_variation_executor(self, variation_executor: VariationExecutor):
|
|
147
|
+
"""
|
|
148
|
+
This method adds a new VariationExecutor to the child element list of the tree
|
|
149
|
+
"""
|
|
150
|
+
if not isinstance(variation_executor, VariationExecutor):
|
|
151
|
+
raise TypeError("the given object `variation_executor` must be of type `VariationExecutor`")
|
|
152
|
+
if variation_executor in self._variation_executors:
|
|
153
|
+
raise ValueError("the given object `variation_executor` already exists in child list")
|
|
154
|
+
self._variation_executors.append(variation_executor)
|
|
155
|
+
|
|
156
|
+
def get_executor_for_device_mapping(self, device_mapping: dict) -> Union[VariationExecutor, None]:
|
|
157
|
+
"""
|
|
158
|
+
This method searches for a VariationExecutor in the internal list for which the given device mapping is
|
|
159
|
+
contained in
|
|
160
|
+
|
|
161
|
+
:param device_mapping: the device_mapping dictionary for which the executor should be searched for
|
|
162
|
+
|
|
163
|
+
:return: returns the associated VariationExecutor or None if no matching could be found
|
|
164
|
+
"""
|
|
165
|
+
for cur_variation_executor in self._variation_executors:
|
|
166
|
+
if cur_variation_executor.base_device_mapping == device_mapping:
|
|
167
|
+
return cur_variation_executor
|
|
168
|
+
# can not find some
|
|
169
|
+
return None
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Type, Union, List, TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
from _balder.fixture_execution_level import FixtureExecutionLevel
|
|
5
|
+
from _balder.testresult import ResultState, BranchBodyResult
|
|
6
|
+
from _balder.executor.basic_executable_executor import BasicExecutableExecutor
|
|
7
|
+
from _balder.executor.scenario_executor import ScenarioExecutor
|
|
8
|
+
from _balder.previous_executor_mark import PreviousExecutorMark
|
|
9
|
+
from _balder.controllers.setup_controller import SetupController
|
|
10
|
+
from _balder.controllers.device_controller import DeviceController
|
|
11
|
+
from _balder.controllers.feature_controller import FeatureController
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from _balder.setup import Setup
|
|
15
|
+
from _balder.scenario import Scenario
|
|
16
|
+
from _balder.fixture_manager import FixtureManager
|
|
17
|
+
from _balder.executor.executor_tree import ExecutorTree
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SetupExecutor(BasicExecutableExecutor):
|
|
21
|
+
"""
|
|
22
|
+
A SetupExecutor is the highest branch object of an :class:`ExecutorTree`. It contains all scenarios and the
|
|
23
|
+
underlying device mappings and their test cases that exist with this setup.
|
|
24
|
+
"""
|
|
25
|
+
fixture_execution_level = FixtureExecutionLevel.SETUP
|
|
26
|
+
|
|
27
|
+
def __init__(self, setup: Type[Setup], parent: ExecutorTree):
|
|
28
|
+
super().__init__()
|
|
29
|
+
self._scenario_executors: List[ScenarioExecutor] = []
|
|
30
|
+
# check if instance already exists
|
|
31
|
+
if hasattr(setup, "_instance") and setup._instance is not None and isinstance(setup._instance, setup):
|
|
32
|
+
self._base_setup_class = setup._instance
|
|
33
|
+
else:
|
|
34
|
+
self._base_setup_class = setup()
|
|
35
|
+
setup._instance = self._base_setup_class
|
|
36
|
+
self._parent_executor = parent
|
|
37
|
+
self._fixture_manager = parent.fixture_manager
|
|
38
|
+
|
|
39
|
+
# contains the result object for the BODY part of this branch
|
|
40
|
+
self.body_result = BranchBodyResult(self)
|
|
41
|
+
|
|
42
|
+
# ---------------------------------- STATIC METHODS ----------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
# ---------------------------------- CLASS METHODS ----------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
# ---------------------------------- PROPERTIES --------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def all_child_executors(self) -> List[ScenarioExecutor]:
|
|
50
|
+
return self._scenario_executors
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def parent_executor(self) -> ExecutorTree:
|
|
54
|
+
return self._parent_executor
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def base_instance(self) -> object:
|
|
58
|
+
"""returns the base class instance to which this executor instance belongs"""
|
|
59
|
+
return self.base_setup_class
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def base_setup_class(self) -> Setup:
|
|
63
|
+
"""returns the base :class:`Setup` that belongs to this executor"""
|
|
64
|
+
return self._base_setup_class
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def base_setup_controller(self) -> SetupController:
|
|
68
|
+
"""returns the :class:`SetupController` for the setup object of this executor"""
|
|
69
|
+
return SetupController.get_for(self.base_setup_class.__class__)
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def fixture_manager(self) -> FixtureManager:
|
|
73
|
+
"""returns the current active fixture manager for this executor"""
|
|
74
|
+
return self._fixture_manager
|
|
75
|
+
|
|
76
|
+
# ---------------------------------- PROTECTED METHODS -------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
def _prepare_execution(self, show_discarded):
|
|
79
|
+
print(f"SETUP {self.base_setup_class.__class__.__name__}")
|
|
80
|
+
|
|
81
|
+
def _body_execution(self, show_discarded):
|
|
82
|
+
for cur_scenario_executor in self.get_scenario_executors():
|
|
83
|
+
prev_mark = cur_scenario_executor.prev_mark
|
|
84
|
+
if cur_scenario_executor.has_runnable_tests(show_discarded) or cur_scenario_executor.has_skipped_tests():
|
|
85
|
+
cur_scenario_executor.execute(show_discarded=show_discarded)
|
|
86
|
+
elif prev_mark == PreviousExecutorMark.SKIP:
|
|
87
|
+
cur_scenario_executor.set_result_for_whole_branch(ResultState.SKIP)
|
|
88
|
+
elif prev_mark == PreviousExecutorMark.COVERED_BY:
|
|
89
|
+
cur_scenario_executor.set_result_for_whole_branch(ResultState.COVERED_BY)
|
|
90
|
+
else:
|
|
91
|
+
cur_scenario_executor.set_result_for_whole_branch(ResultState.NOT_RUN)
|
|
92
|
+
|
|
93
|
+
def _cleanup_execution(self, show_discarded):
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
# ---------------------------------- METHODS -----------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
def update_inner_referenced_feature_instances(self):
|
|
99
|
+
"""
|
|
100
|
+
This method ensures that all inner referenced feature instances of all devices that are used in this setup are
|
|
101
|
+
set to the correct feature instance. For every existing device, this method goes trough all assigned features
|
|
102
|
+
and checks for a inner-referenced feature. It makes sure, that every inner-referenced feature variable has the
|
|
103
|
+
final assigned setup feature that belongs to it.
|
|
104
|
+
|
|
105
|
+
# TODO check where we validate that inner references feature exists in setup
|
|
106
|
+
"""
|
|
107
|
+
for cur_setup_device in self.base_setup_controller.get_all_abs_inner_device_classes():
|
|
108
|
+
# these features are subclasses of the real defined one (because they are already the replaced ones)
|
|
109
|
+
all_device_features = DeviceController.get_for(cur_setup_device).get_all_instantiated_feature_objects()
|
|
110
|
+
all_instantiated_feature_objs_of_this_dev = [cur_feature for _, cur_feature in all_device_features.items()]
|
|
111
|
+
for _, cur_feature in all_device_features.items():
|
|
112
|
+
cur_feature_controller = FeatureController.get_for(cur_feature.__class__)
|
|
113
|
+
# now check the inner referenced features of this feature and check if that exists in the device
|
|
114
|
+
for cur_ref_feature_name, cur_ref_feature in \
|
|
115
|
+
cur_feature_controller.get_inner_referenced_features().items():
|
|
116
|
+
potential_candidates = [
|
|
117
|
+
candidate_feature for candidate_feature in all_instantiated_feature_objs_of_this_dev
|
|
118
|
+
if isinstance(candidate_feature, cur_ref_feature.__class__)]
|
|
119
|
+
if len(potential_candidates) != 1:
|
|
120
|
+
raise RuntimeError("found none or more than one potential replacing candidates")
|
|
121
|
+
replacing_candidate = potential_candidates[0]
|
|
122
|
+
# because `cur_feature` is only the object instance, the value will be overwritten only for this
|
|
123
|
+
# object
|
|
124
|
+
setattr(cur_feature, cur_ref_feature_name, replacing_candidate)
|
|
125
|
+
|
|
126
|
+
def get_scenario_executors(self) -> List[ScenarioExecutor]:
|
|
127
|
+
"""returns a list with all scenario executors that belongs to this setup executor"""
|
|
128
|
+
return self._scenario_executors
|
|
129
|
+
|
|
130
|
+
def cleanup_empty_executor_branches(self, consider_discarded=False):
|
|
131
|
+
to_remove_executor = []
|
|
132
|
+
for cur_scenario_executor in self.get_scenario_executors():
|
|
133
|
+
cur_scenario_executor.cleanup_empty_executor_branches(consider_discarded=consider_discarded)
|
|
134
|
+
if len(cur_scenario_executor.get_variation_executors(return_discarded=consider_discarded)) == 0:
|
|
135
|
+
# remove this whole executor because it has no children anymore
|
|
136
|
+
to_remove_executor.append(cur_scenario_executor)
|
|
137
|
+
for cur_scenario_executor in to_remove_executor:
|
|
138
|
+
self._scenario_executors.remove(cur_scenario_executor)
|
|
139
|
+
|
|
140
|
+
def add_scenario_executor(self, scenario_executor: ScenarioExecutor):
|
|
141
|
+
"""
|
|
142
|
+
This method adds a new ScenarioExecutor to the child element list of the tree
|
|
143
|
+
"""
|
|
144
|
+
if not isinstance(scenario_executor, ScenarioExecutor):
|
|
145
|
+
raise TypeError("the given object `scenario_executor` must be of type type `ScenarioExecutor`")
|
|
146
|
+
if scenario_executor in self._scenario_executors:
|
|
147
|
+
raise ValueError("the given object `scenario_executor` already exists in child list")
|
|
148
|
+
self._scenario_executors.append(scenario_executor)
|
|
149
|
+
|
|
150
|
+
def get_executor_for_scenario(self, scenario: Type[Scenario]) -> Union[ScenarioExecutor, None]:
|
|
151
|
+
"""
|
|
152
|
+
This method searches for a ScenarioExecutor in the internal list for which the given scenario is
|
|
153
|
+
contained in
|
|
154
|
+
|
|
155
|
+
:param scenario: the scenario class for which the executor should be searched for
|
|
156
|
+
|
|
157
|
+
:return: returns the associated ScenarioExecutor or None if no matching could be found
|
|
158
|
+
"""
|
|
159
|
+
for cur_scenario_executor in self._scenario_executors:
|
|
160
|
+
if cur_scenario_executor.base_scenario_class.__class__ == scenario:
|
|
161
|
+
return cur_scenario_executor
|
|
162
|
+
# can not find some
|
|
163
|
+
return None
|