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,882 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Type, Union, List, Dict, TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
import inspect
|
|
6
|
+
import logging
|
|
7
|
+
from _balder.cnnrelations import OrConnectionRelation
|
|
8
|
+
from _balder.device import Device
|
|
9
|
+
from _balder.connection import Connection
|
|
10
|
+
from _balder.feature_replacement_mapping import FeatureReplacementMapping
|
|
11
|
+
from _balder.fixture_execution_level import FixtureExecutionLevel
|
|
12
|
+
from _balder.testresult import ResultState, BranchBodyResult, ResultSummary
|
|
13
|
+
from _balder.executor.basic_executable_executor import BasicExecutableExecutor
|
|
14
|
+
from _balder.executor.testcase_executor import TestcaseExecutor
|
|
15
|
+
from _balder.executor.unresolved_parametrized_testcase_executor import UnresolvedParametrizedTestcaseExecutor
|
|
16
|
+
from _balder.previous_executor_mark import PreviousExecutorMark
|
|
17
|
+
from _balder.routing_path import RoutingPath
|
|
18
|
+
from _balder.unmapped_vdevice import UnmappedVDevice
|
|
19
|
+
from _balder.feature_vdevice_mapping import FeatureVDeviceMapping
|
|
20
|
+
from _balder.controllers import DeviceController, VDeviceController, FeatureController, NormalScenarioSetupController
|
|
21
|
+
from _balder.exceptions import NotApplicableVariationException, UnclearAssignableFeatureConnectionError
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from _balder.setup import Setup
|
|
25
|
+
from _balder.feature import Feature
|
|
26
|
+
from _balder.scenario import Scenario
|
|
27
|
+
from _balder.controllers.scenario_controller import ScenarioController
|
|
28
|
+
from _balder.controllers.setup_controller import SetupController
|
|
29
|
+
from _balder.executor.scenario_executor import ScenarioExecutor
|
|
30
|
+
from _balder.fixture_manager import FixtureManager
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
logger = logging.getLogger(__file__)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class VariationExecutor(BasicExecutableExecutor):
|
|
37
|
+
"""
|
|
38
|
+
A VariationExecutor only contains :meth:`TestcaseExecutor` children.
|
|
39
|
+
"""
|
|
40
|
+
fixture_execution_level = FixtureExecutionLevel.VARIATION
|
|
41
|
+
|
|
42
|
+
def __init__(self, device_mapping: Dict[Type[Device], Type[Device]], parent: ScenarioExecutor):
|
|
43
|
+
super().__init__()
|
|
44
|
+
self._testcase_executors = []
|
|
45
|
+
self._base_device_mapping = device_mapping
|
|
46
|
+
self._parent_executor = parent
|
|
47
|
+
self._fixture_manager = parent.fixture_manager
|
|
48
|
+
|
|
49
|
+
# contains the active routings for the current variation
|
|
50
|
+
self._routings: Dict[Connection, List[RoutingPath]] = {}
|
|
51
|
+
# buffer variable to save the feature replacement after it was determined with
|
|
52
|
+
# `determine_feature_replacement_and_vdevice_mappings()`
|
|
53
|
+
self._feature_replacement: Union[None, Dict[Type[Device], FeatureReplacementMapping]] = None
|
|
54
|
+
# buffer variable to save the feature replacement after it was determined with
|
|
55
|
+
# `determine_feature_replacement_and_vdevice_mappings()`
|
|
56
|
+
self._abs_setup_feature_vdevice_mappings: Union[None, Dict[Type[Device], FeatureVDeviceMapping]] = None
|
|
57
|
+
|
|
58
|
+
# contains the absolute scenario device connections for the current variation
|
|
59
|
+
self._abs_variation_scenario_device_connections: Union[List[Connection], None] = None
|
|
60
|
+
# contains the absolute active variation connections (intersection between scenario based
|
|
61
|
+
# `_abs_variation_scenario_device_connections` and virtual connection from active RoutingPath objects from
|
|
62
|
+
# `_routings`)
|
|
63
|
+
self._abs_variation_connections: Union[List[Connection], None] = None
|
|
64
|
+
|
|
65
|
+
# contains the original active vdevice mappings for all scenario and setup devices (will be managed by
|
|
66
|
+
# `update_active_vdevice_device_mappings_in_scenario_and_setup_devices()` and
|
|
67
|
+
# `revert_active_vdevice_device_mappings_in_scenario_and_setup_devices()`)
|
|
68
|
+
self._original_active_vdevice_mappings: Dict[Type[Device], FeatureVDeviceMapping] = {}
|
|
69
|
+
|
|
70
|
+
# is True if the applicability check was done
|
|
71
|
+
self._applicability_check_done = False
|
|
72
|
+
#: this property holds the exception of type :class:`NotApplicableVariationException` if the variation can not
|
|
73
|
+
#: be applied, otherwise this property is None
|
|
74
|
+
self._not_applicable_variation_exc = None
|
|
75
|
+
|
|
76
|
+
# contains the result object for the BODY part of this branch
|
|
77
|
+
self.body_result = BranchBodyResult(self)
|
|
78
|
+
|
|
79
|
+
# ---------------------------------- STATIC METHODS ----------------------------------------------------------------
|
|
80
|
+
|
|
81
|
+
# ---------------------------------- CLASS METHODS ----------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
# ---------------------------------- PROPERTIES --------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def base_instance(self) -> object:
|
|
87
|
+
"""
|
|
88
|
+
returns the base class instance to which this executor instance belongs to
|
|
89
|
+
"""
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def parent_executor(self) -> ScenarioExecutor:
|
|
94
|
+
return self._parent_executor
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def cur_scenario_class(self) -> Scenario:
|
|
98
|
+
"""property returns the current :class:`Scenario` for this variation"""
|
|
99
|
+
return self._parent_executor.base_scenario_class
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def cur_scenario_controller(self) -> ScenarioController:
|
|
103
|
+
"""
|
|
104
|
+
returns the current :class:`ScenarioController` for this variation
|
|
105
|
+
"""
|
|
106
|
+
return self._parent_executor.base_scenario_controller
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def cur_setup_class(self) -> Setup:
|
|
110
|
+
"""property returns the current :class:`Setup` for this variation"""
|
|
111
|
+
return self._parent_executor.parent_executor.base_setup_class
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def cur_setup_controller(self) -> SetupController:
|
|
115
|
+
"""
|
|
116
|
+
returns the current :class:`SetupController` for this variation
|
|
117
|
+
"""
|
|
118
|
+
return self._parent_executor.parent_executor.base_setup_controller
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def base_device_mapping(self) -> Dict[Type[Device], Type[Device]]:
|
|
122
|
+
"""
|
|
123
|
+
property returns the device mapping which is a dictionary with the scenario devices as keys and their related
|
|
124
|
+
setup devices as values
|
|
125
|
+
"""
|
|
126
|
+
return self._base_device_mapping
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def all_child_executors(self) -> List[TestcaseExecutor | UnresolvedParametrizedTestcaseExecutor]:
|
|
130
|
+
return self._testcase_executors
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def fixture_manager(self) -> FixtureManager:
|
|
134
|
+
"""returns the active fixture manager"""
|
|
135
|
+
return self._fixture_manager
|
|
136
|
+
|
|
137
|
+
@property
|
|
138
|
+
def feature_replacement(self) -> Dict[Type[Device], FeatureReplacementMapping]:
|
|
139
|
+
"""
|
|
140
|
+
this property is a dictionary with every scenario device as key and feature-replacement-mapping as value - the
|
|
141
|
+
mappings hold at least information about the attribute name of the feature in scenario device, the old
|
|
142
|
+
scenario-feature the instantiated feature from the scenario if it exists, otherwise this is None) and the
|
|
143
|
+
new feature as second item (the feature of the related Setup-Device)
|
|
144
|
+
"""
|
|
145
|
+
return self._feature_replacement
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def abs_setup_feature_vdevice_mappings(self) -> Dict[Type[Device], FeatureVDeviceMapping]:
|
|
149
|
+
"""returns the feature replacement that was determined with
|
|
150
|
+
`determine_feature_replacement_and_vdevice_mappings()`"""
|
|
151
|
+
return self._abs_setup_feature_vdevice_mappings
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def not_applicable_variation_exc(self) -> Union[NotApplicableVariationException, None]:
|
|
155
|
+
"""holds the :class:`NotApplicableVariationException` that describes why this variation in not applicable"""
|
|
156
|
+
return self._not_applicable_variation_exc
|
|
157
|
+
|
|
158
|
+
# ---------------------------------- PROTECTED METHODS -------------------------------------------------------------
|
|
159
|
+
|
|
160
|
+
def _prepare_execution(self, show_discarded):
|
|
161
|
+
print(" VARIATION ", end='')
|
|
162
|
+
device_map_str = [f"{scenario_device.__qualname__}:{setup_device.__qualname__}"
|
|
163
|
+
for scenario_device, setup_device in self._base_device_mapping.items()]
|
|
164
|
+
print(' | '.join(device_map_str))
|
|
165
|
+
if show_discarded and not self.can_be_applied():
|
|
166
|
+
print(f" DISCARDED BECAUSE `{self.not_applicable_variation_exc.args[0]}`")
|
|
167
|
+
else:
|
|
168
|
+
self.determine_abs_variation_connections()
|
|
169
|
+
self.update_scenario_device_feature_instances()
|
|
170
|
+
self.update_active_vdevice_device_mappings_in_all_features()
|
|
171
|
+
self.exchange_unmapped_vdevice_references()
|
|
172
|
+
self.update_vdevice_referenced_feature_instances()
|
|
173
|
+
self.set_conn_dependent_methods()
|
|
174
|
+
self.resolve_and_exchange_unresolved_parametrization()
|
|
175
|
+
|
|
176
|
+
def _body_execution(self, show_discarded):
|
|
177
|
+
if show_discarded and not self.can_be_applied():
|
|
178
|
+
# do nothing if this variation can not be applied (is discarded)
|
|
179
|
+
return
|
|
180
|
+
|
|
181
|
+
for cur_testcase_executor in self.get_testcase_executors():
|
|
182
|
+
if (cur_testcase_executor.has_runnable_tests()
|
|
183
|
+
or cur_testcase_executor.has_skipped_tests()
|
|
184
|
+
or cur_testcase_executor.has_covered_by_tests()):
|
|
185
|
+
cur_testcase_executor.execute()
|
|
186
|
+
else:
|
|
187
|
+
cur_testcase_executor.set_result_for_whole_branch(ResultState.NOT_RUN)
|
|
188
|
+
|
|
189
|
+
def _cleanup_execution(self, show_discarded):
|
|
190
|
+
if show_discarded and not self.can_be_applied():
|
|
191
|
+
# do nothing if this variation can not be applied (is discarded)
|
|
192
|
+
return
|
|
193
|
+
|
|
194
|
+
self.restore_original_vdevice_references()
|
|
195
|
+
self.revert_active_vdevice_device_mappings_in_all_features()
|
|
196
|
+
self.revert_scenario_device_feature_instances()
|
|
197
|
+
|
|
198
|
+
def _verify_applicability_trough_feature_implementation_matching(self):
|
|
199
|
+
"""
|
|
200
|
+
This method validates, that the features in this variation are valid. For this the setup devices must
|
|
201
|
+
implement all the required features of the scenario device. In other words, a feature child class must be
|
|
202
|
+
implemented in the setup device for each feature in the scenario device.
|
|
203
|
+
"""
|
|
204
|
+
for scenario_device, setup_device in self._base_device_mapping.items():
|
|
205
|
+
scenario_device_orig_features = \
|
|
206
|
+
DeviceController.get_for(scenario_device).get_original_instanced_feature_objects()
|
|
207
|
+
for cur_scenario_feature_attr_name, cur_scenario_feature in scenario_device_orig_features.items():
|
|
208
|
+
found_setup_feature_for_scenario_feature = False
|
|
209
|
+
setup_device_instantiated_features = \
|
|
210
|
+
DeviceController.get_for(setup_device).get_all_instantiated_feature_objects()
|
|
211
|
+
for _, cur_setup_feature in setup_device_instantiated_features.items():
|
|
212
|
+
if isinstance(cur_setup_feature, cur_scenario_feature.__class__):
|
|
213
|
+
found_setup_feature_for_scenario_feature = True
|
|
214
|
+
if not found_setup_feature_for_scenario_feature:
|
|
215
|
+
raise NotApplicableVariationException(
|
|
216
|
+
f'no matching setup-level feature found for scenario-level feature '
|
|
217
|
+
f'`{cur_scenario_feature_attr_name} = {cur_scenario_feature.__class__.__name__}()` of device '
|
|
218
|
+
f'`{scenario_device.__qualname__}`')
|
|
219
|
+
|
|
220
|
+
def _verify_applicability_trough_vdevice_feature_impl_matching(self) -> None:
|
|
221
|
+
"""
|
|
222
|
+
This method checks for all vDevices that are in the setups/scenario features of this VariationExecutor, if their
|
|
223
|
+
vDevices-Mappings (so the mapped setup-devices) implements all features that are defined in the vDevices
|
|
224
|
+
"""
|
|
225
|
+
|
|
226
|
+
for cur_scenario_device, cur_replacement_mapping in self.feature_replacement.items():
|
|
227
|
+
cur_setup_device = self.get_setup_device_for(scenario_device=cur_scenario_device)
|
|
228
|
+
all_inner_setup_features = \
|
|
229
|
+
DeviceController.get_for(cur_setup_device).get_all_instantiated_feature_objects()
|
|
230
|
+
|
|
231
|
+
# now secure that all features are available in the corresponding setup device, that are defined in the
|
|
232
|
+
# mapped vDevice
|
|
233
|
+
for _, cur_setup_feature_obj in all_inner_setup_features.items():
|
|
234
|
+
related_scenario_feature_obj = \
|
|
235
|
+
cur_replacement_mapping.get_replaced_scenario_feature_for(cur_setup_feature_obj)
|
|
236
|
+
|
|
237
|
+
# only check if this feature is required by the scenario
|
|
238
|
+
if related_scenario_feature_obj is None:
|
|
239
|
+
# ignore this, because this feature is not used in the scenario
|
|
240
|
+
continue
|
|
241
|
+
|
|
242
|
+
# get vDevice and device mapping
|
|
243
|
+
partner_scenario_vdevice, partner_scenario_device = \
|
|
244
|
+
related_scenario_feature_obj.active_vdevice_device_mapping
|
|
245
|
+
|
|
246
|
+
if partner_scenario_device is None:
|
|
247
|
+
# ignore because no mapping exist here
|
|
248
|
+
continue
|
|
249
|
+
|
|
250
|
+
partner_setup_device = self.get_setup_device_for(scenario_device=partner_scenario_device)
|
|
251
|
+
# get the related vDevice on setup view that is currently active
|
|
252
|
+
mapped_setup_vdevices = [
|
|
253
|
+
cur_vdevice for cur_vdevice
|
|
254
|
+
in FeatureController.get_for(
|
|
255
|
+
cur_setup_feature_obj.__class__).get_abs_inner_vdevice_classes()
|
|
256
|
+
if issubclass(cur_vdevice, partner_scenario_vdevice)]
|
|
257
|
+
if len(mapped_setup_vdevices) != 1:
|
|
258
|
+
# find no mapping for the vDevice -> not possible
|
|
259
|
+
# todo optimize this exception message
|
|
260
|
+
raise NotApplicableVariationException(
|
|
261
|
+
f'can not find a valid setup-level vDevice in setup feature '
|
|
262
|
+
f'`{cur_setup_feature_obj.__class__}`')
|
|
263
|
+
# now check that the setup partner device has all features implemented that are required
|
|
264
|
+
# features from the VDevice
|
|
265
|
+
partner_setup_device_features = \
|
|
266
|
+
DeviceController.get_for(partner_setup_device).get_all_instantiated_feature_objects()
|
|
267
|
+
mapped_setup_vdevices_instantiated_features = \
|
|
268
|
+
VDeviceController.get_for(mapped_setup_vdevices[0]).get_all_instantiated_feature_objects()
|
|
269
|
+
for _, cur_vdevice_feature in mapped_setup_vdevices_instantiated_features.items():
|
|
270
|
+
# check that there exists a child feature in the setup device for every used feature in the
|
|
271
|
+
# vDevice class
|
|
272
|
+
if len([cur_device_feature for _, cur_device_feature in partner_setup_device_features.items()
|
|
273
|
+
if isinstance(cur_device_feature, cur_vdevice_feature.__class__)]) == 0:
|
|
274
|
+
raise NotApplicableVariationException(
|
|
275
|
+
f'can not find a child feature in mapped setup device `{partner_setup_device.__qualname__}`'
|
|
276
|
+
f' for required feature `{cur_vdevice_feature.__class__}` of vDevice '
|
|
277
|
+
f'`{mapped_setup_vdevices[0].__qualname__}`')
|
|
278
|
+
|
|
279
|
+
def _verify_applicability_trough_all_valid_routings(self) -> None:
|
|
280
|
+
"""
|
|
281
|
+
This method ensures that valid routings exist for every defined connection.
|
|
282
|
+
|
|
283
|
+
The check is passed, if the method finds one or more valid routings for EVERY scenario-level
|
|
284
|
+
:class:`Connection`.
|
|
285
|
+
"""
|
|
286
|
+
if not self._routings:
|
|
287
|
+
self.determine_absolute_scenario_device_connections()
|
|
288
|
+
self.create_all_valid_routings()
|
|
289
|
+
for scenario_cnn, cur_routings in self._routings.items():
|
|
290
|
+
if len(cur_routings) == 0:
|
|
291
|
+
raise NotApplicableVariationException(
|
|
292
|
+
f'can not find a valid routing on setup level for the connection `{scenario_cnn.get_tree_str()}` '
|
|
293
|
+
f'between scenario devices `{scenario_cnn.from_device}` and `{scenario_cnn.to_device}`')
|
|
294
|
+
|
|
295
|
+
def _get_matching_setup_features_for(
|
|
296
|
+
self,
|
|
297
|
+
scenario_feature_obj: Feature,
|
|
298
|
+
in_setup_device: Type[Device]
|
|
299
|
+
) -> List[Feature]:
|
|
300
|
+
"""
|
|
301
|
+
Helper method that returns all matching setup features for the provided scenario feature in the provided setup
|
|
302
|
+
device.
|
|
303
|
+
"""
|
|
304
|
+
cur_setup_features = DeviceController.get_for(in_setup_device).get_all_instantiated_feature_objects()
|
|
305
|
+
|
|
306
|
+
replacing_feature_candidates = [
|
|
307
|
+
cur_setup_feature for cur_setup_feature in cur_setup_features.values()
|
|
308
|
+
if isinstance(cur_setup_feature, scenario_feature_obj.__class__)
|
|
309
|
+
]
|
|
310
|
+
active_scenario_vdev, mapped_scenario_dev = scenario_feature_obj.active_vdevice_device_mapping
|
|
311
|
+
|
|
312
|
+
replacing_features = replacing_feature_candidates.copy()
|
|
313
|
+
if mapped_scenario_dev is not None:
|
|
314
|
+
# get the related setup device for the mapped scenario device (on scenario level)
|
|
315
|
+
setup_dev_of_mapped_scenario_dev = self.get_setup_device_for(mapped_scenario_dev)
|
|
316
|
+
|
|
317
|
+
# now check if there is a mapping on setup level too
|
|
318
|
+
for cur_replacing_feature in replacing_feature_candidates:
|
|
319
|
+
mapped_setup_vdev, mapped_setup_dev = cur_replacing_feature.active_vdevice_device_mapping
|
|
320
|
+
if mapped_setup_vdev is not None and not issubclass(mapped_setup_vdev, active_scenario_vdev):
|
|
321
|
+
# drop this feature matching, because we have different vdevice mapped
|
|
322
|
+
replacing_features.remove(cur_replacing_feature)
|
|
323
|
+
elif mapped_setup_dev is not None and mapped_setup_dev != setup_dev_of_mapped_scenario_dev:
|
|
324
|
+
# drop this feature matching, because it is not applicable here
|
|
325
|
+
replacing_features.remove(cur_replacing_feature)
|
|
326
|
+
return replacing_features
|
|
327
|
+
|
|
328
|
+
# ---------------------------------- METHODS -----------------------------------------------------------------------
|
|
329
|
+
|
|
330
|
+
def testsummary(self) -> ResultSummary:
|
|
331
|
+
if self.can_be_applied():
|
|
332
|
+
return super().testsummary()
|
|
333
|
+
return ResultSummary()
|
|
334
|
+
|
|
335
|
+
def get_testcase_executors(self) -> List[TestcaseExecutor | UnresolvedParametrizedTestcaseExecutor]:
|
|
336
|
+
"""returns all sub testcase executors that belongs to this variation-executor"""
|
|
337
|
+
return self._testcase_executors.copy()
|
|
338
|
+
|
|
339
|
+
def add_testcase_executor(self, testcase_executor: TestcaseExecutor | UnresolvedParametrizedTestcaseExecutor):
|
|
340
|
+
"""
|
|
341
|
+
This method adds a new TestcaseExecutor to the child element list of this object branch
|
|
342
|
+
"""
|
|
343
|
+
if not isinstance(testcase_executor, (TestcaseExecutor, UnresolvedParametrizedTestcaseExecutor)):
|
|
344
|
+
raise TypeError("the given object `testcase_executor` must be of type type `TestcaseExecutor` or "
|
|
345
|
+
"`UnresolvedParametrizedTestcaseExecutor`")
|
|
346
|
+
if testcase_executor in self._testcase_executors:
|
|
347
|
+
raise ValueError("the given object `testcase_executor` already exists in child list")
|
|
348
|
+
self._testcase_executors.append(testcase_executor)
|
|
349
|
+
|
|
350
|
+
def determine_feature_replacement_and_vdevice_mappings(self) -> None:
|
|
351
|
+
"""
|
|
352
|
+
This method determines the :class:`Feature` replacement and the absolute vdevice mappings for this variation and
|
|
353
|
+
its related features. It determines for every existing scenario-device-feature the related setup-device-feature
|
|
354
|
+
class and sets this information to the property `_feature_replacement`. In addition to that it also determines
|
|
355
|
+
the vDevice-Device mapping that will be assigned for every replaced feature later. This information will be
|
|
356
|
+
saved in the property `_abs_vdevice_mappings`.
|
|
357
|
+
|
|
358
|
+
.. note::
|
|
359
|
+
If there is a feature class in the setup device, that is not used in the mapped scenario device, this
|
|
360
|
+
feature will be added as **Autonomous-Feature**. This is required, because the features could be referenced
|
|
361
|
+
in a lower child class than the **instantiated scenario feature**.
|
|
362
|
+
|
|
363
|
+
.. note::
|
|
364
|
+
The method automatically searches for the correct mappings. So if there exists more than one possible
|
|
365
|
+
mappings for a feature, the method checks for the defined mapping on scenario level and also on setup level.
|
|
366
|
+
If there are no or more than one possible mappings, the method raises a
|
|
367
|
+
:class:`NotApplicableVariationError`. This error will also be thrown, if there is a vDevice mapping
|
|
368
|
+
expected, while no vDevice mapping exists.
|
|
369
|
+
|
|
370
|
+
:raises NotApplicableVariationError: will be thrown if this variation cannot be applied, because the setup-/
|
|
371
|
+
scenario-device-features can not be resolved
|
|
372
|
+
"""
|
|
373
|
+
feature_replacement = {
|
|
374
|
+
scenario_dev: FeatureReplacementMapping() for scenario_dev in self.base_device_mapping.keys()
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
abs_setup_vdevice_mappings = {
|
|
378
|
+
setup_dev: FeatureVDeviceMapping() for setup_dev in self.base_device_mapping.values()
|
|
379
|
+
}
|
|
380
|
+
for cur_scenario_device, cur_setup_device in self.base_device_mapping.items():
|
|
381
|
+
|
|
382
|
+
for cur_attr_name, cur_scenario_feature_obj in \
|
|
383
|
+
DeviceController.get_for(cur_scenario_device).get_all_instantiated_feature_objects().items():
|
|
384
|
+
active_scenario_vdevice, mapped_scenario_device = cur_scenario_feature_obj.active_vdevice_device_mapping
|
|
385
|
+
|
|
386
|
+
cur_setup_feature_objs = self._get_matching_setup_features_for(
|
|
387
|
+
scenario_feature_obj=cur_scenario_feature_obj, in_setup_device=cur_setup_device
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
if len(cur_setup_feature_objs) != 1:
|
|
391
|
+
raise NotApplicableVariationException(
|
|
392
|
+
f'this variation can not be applicable because there was no setup feature implementation of '
|
|
393
|
+
f'`{cur_scenario_feature_obj.__class__.__name__}` (used by scenario device '
|
|
394
|
+
f'`{cur_scenario_device.__name__}`) in setup device `{cur_setup_device.__name__}`')
|
|
395
|
+
cur_setup_feature_obj = cur_setup_feature_objs[0]
|
|
396
|
+
|
|
397
|
+
all_abs_inner_vdevs_of_setup = \
|
|
398
|
+
FeatureController.get_for(cur_setup_feature_obj.__class__).get_abs_inner_vdevice_classes()
|
|
399
|
+
used_setup_vdevice, mapped_setup_device = cur_setup_feature_obj.active_vdevice_device_mapping
|
|
400
|
+
|
|
401
|
+
if mapped_scenario_device is None:
|
|
402
|
+
# we have exactly one matching candidate, but also no vDevice mapping
|
|
403
|
+
# check if the matching candidate has a vDevice mapping
|
|
404
|
+
if mapped_setup_device is None and len(all_abs_inner_vdevs_of_setup) > 0:
|
|
405
|
+
# there is no vDevice mapping on scenario and no vDevice mapping on setup level, but the
|
|
406
|
+
# feature defined vDevices -> NOT APPLICABLE
|
|
407
|
+
logger.warning(
|
|
408
|
+
f"missing vDevice mapping for feature "
|
|
409
|
+
f"`{cur_scenario_feature_obj.__class__.__name__}` (used in scenario device "
|
|
410
|
+
f"`{cur_scenario_device.__name__}` and in setup device `{cur_setup_device.__name__}`) - "
|
|
411
|
+
f"VARIATION CAN NOT BE APPLIED")
|
|
412
|
+
raise NotApplicableVariationException(
|
|
413
|
+
f'this variation can not be applied because there was no vDevice mapping given on '
|
|
414
|
+
f'scenario or on setup level for the feature '
|
|
415
|
+
f'`{cur_scenario_feature_obj.__class__.__name__}` (used by scenario device '
|
|
416
|
+
f'`{cur_scenario_device.__name__}`) in setup device `{cur_setup_device.__name__}`')
|
|
417
|
+
|
|
418
|
+
if cur_attr_name not in feature_replacement[cur_scenario_device].attr_names:
|
|
419
|
+
|
|
420
|
+
# if there is a vDevice mapping on scenario level, but not on setup level, so update the
|
|
421
|
+
# VDevice-Device-Mapping there
|
|
422
|
+
if mapped_scenario_device is not None and mapped_setup_device is None:
|
|
423
|
+
# search the equivalent vDevice on setup level and use this one (we had not to check it,
|
|
424
|
+
# because check was already done in collector-stage)
|
|
425
|
+
setup_vdevices = [cur_vdevice for cur_vdevice in all_abs_inner_vdevs_of_setup
|
|
426
|
+
if cur_vdevice.__name__ == active_scenario_vdevice.__name__]
|
|
427
|
+
used_setup_vdevice = setup_vdevices[0]
|
|
428
|
+
# set the mapping
|
|
429
|
+
abs_setup_vdevice_mappings[cur_setup_device].add(
|
|
430
|
+
feature=cur_setup_feature_obj,
|
|
431
|
+
mappings={
|
|
432
|
+
used_setup_vdevice: self.get_setup_device_for(mapped_scenario_device)
|
|
433
|
+
}
|
|
434
|
+
)
|
|
435
|
+
# if there is a vDevice mapping on setup level, but not on scenario level, so directly update the
|
|
436
|
+
# VDevice-Device-Mapping there
|
|
437
|
+
elif mapped_scenario_device is None and mapped_setup_device is not None:
|
|
438
|
+
abs_setup_vdevice_mappings[cur_setup_device].add(
|
|
439
|
+
feature=cur_setup_feature_obj,
|
|
440
|
+
mappings={
|
|
441
|
+
used_setup_vdevice: mapped_setup_device
|
|
442
|
+
}
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
feature_replacement[cur_scenario_device].add(attr_name=cur_attr_name,
|
|
446
|
+
scenario_feature=cur_scenario_feature_obj,
|
|
447
|
+
setup_feature=cur_setup_feature_obj)
|
|
448
|
+
|
|
449
|
+
feature_replacement[cur_scenario_device].add_remaining_setup_features_as_autonomous(cur_setup_device)
|
|
450
|
+
|
|
451
|
+
# set the result to internal properties
|
|
452
|
+
self._feature_replacement = feature_replacement
|
|
453
|
+
self._abs_setup_feature_vdevice_mappings = abs_setup_vdevice_mappings
|
|
454
|
+
|
|
455
|
+
def get_setup_device_for(self, scenario_device: Type[Device]) -> Type[Device]:
|
|
456
|
+
"""
|
|
457
|
+
This method returns the corresponding Setup-Device for the given Scenario-Device that is contained in the
|
|
458
|
+
mapping of this VariationExecutor.
|
|
459
|
+
|
|
460
|
+
:param scenario_device: the scenario device for that the mapped setup device should be returned
|
|
461
|
+
|
|
462
|
+
:return: the mapped setup device
|
|
463
|
+
"""
|
|
464
|
+
if scenario_device not in self.base_device_mapping.keys():
|
|
465
|
+
raise ValueError(
|
|
466
|
+
f"the given scenario device `{scenario_device.__name__}` is no part of the mapping of this "
|
|
467
|
+
f"VariationExecutor object")
|
|
468
|
+
return self.base_device_mapping[scenario_device]
|
|
469
|
+
|
|
470
|
+
def get_scenario_device_for(self, setup_device: Type[Device]) -> Type[Device]:
|
|
471
|
+
"""
|
|
472
|
+
This method returns the corresponding Scenario-Device for the given Setup-Device that is contained in the
|
|
473
|
+
mapping of this VariationExecutor.
|
|
474
|
+
|
|
475
|
+
:param setup_device: the setup device for that the mapped scenario device should be returned
|
|
476
|
+
|
|
477
|
+
:return: the mapped scenario device
|
|
478
|
+
"""
|
|
479
|
+
if setup_device not in self.base_device_mapping.values():
|
|
480
|
+
raise ValueError(
|
|
481
|
+
f"the given scenario device `{setup_device.__name__}` is no part of the mapping of this "
|
|
482
|
+
f"VariationExecutor object")
|
|
483
|
+
if list(self.base_device_mapping.values()).count(setup_device) > 1:
|
|
484
|
+
raise KeyError("the requested setup device exists more than one time in `base_device_mapping`")
|
|
485
|
+
return [cur_key for cur_key, cur_value in self.base_device_mapping.items() if cur_value == setup_device][0]
|
|
486
|
+
|
|
487
|
+
def get_executor_for_testcase(self, testcase: callable) -> TestcaseExecutor | None:
|
|
488
|
+
"""
|
|
489
|
+
This method searches for a TestcaseExecutor in the internal list for which the given testcase method is
|
|
490
|
+
contained in
|
|
491
|
+
|
|
492
|
+
:param testcase: the testcase class for which the executor should be searched for
|
|
493
|
+
|
|
494
|
+
:return: returns the associated TestcaseExecutor or None if none could be found for the transferred type
|
|
495
|
+
"""
|
|
496
|
+
for cur_testcase_executor in self._testcase_executors:
|
|
497
|
+
if cur_testcase_executor.base_testcase_callable == testcase:
|
|
498
|
+
return cur_testcase_executor
|
|
499
|
+
# can not find some
|
|
500
|
+
return None
|
|
501
|
+
|
|
502
|
+
def cleanup_empty_executor_branches(self, consider_discarded=False):
|
|
503
|
+
"""
|
|
504
|
+
This method searches the whole tree and removes branches where an executor item has no own children. It can
|
|
505
|
+
remove these branches, because they have no valid matchings.
|
|
506
|
+
|
|
507
|
+
This method implementation of the :class:`VariationExecutor` does nothing.
|
|
508
|
+
"""
|
|
509
|
+
|
|
510
|
+
def update_scenario_device_feature_instances(self):
|
|
511
|
+
"""
|
|
512
|
+
This method ensures that the (mostly abstract) feature instances of a scenario are exchanged with the
|
|
513
|
+
feature instances of the assigned setup devices
|
|
514
|
+
"""
|
|
515
|
+
for cur_scenario_device, cur_replacement_mapping in self.feature_replacement.items():
|
|
516
|
+
for cur_feature_mapping in cur_replacement_mapping.mappings:
|
|
517
|
+
setattr(cur_scenario_device, cur_feature_mapping.attr_name, cur_feature_mapping.setup_feature)
|
|
518
|
+
|
|
519
|
+
def revert_scenario_device_feature_instances(self):
|
|
520
|
+
"""
|
|
521
|
+
This method ensures that all initialized feature instances of a scenario are set back to the initial given
|
|
522
|
+
features.
|
|
523
|
+
"""
|
|
524
|
+
for cur_scenario_device, cur_replacement_mapping in self.feature_replacement.items():
|
|
525
|
+
for cur_feature_mapping in cur_replacement_mapping.mappings:
|
|
526
|
+
setattr(cur_scenario_device, cur_feature_mapping.attr_name, cur_feature_mapping.scenario_feature)
|
|
527
|
+
|
|
528
|
+
def update_active_vdevice_device_mappings_in_all_features(self):
|
|
529
|
+
"""
|
|
530
|
+
This method ensures that the correct `active_vdevices` property in all feature classes of the related setup and
|
|
531
|
+
scenario-device classes are set correctly.
|
|
532
|
+
"""
|
|
533
|
+
|
|
534
|
+
for cur_setup_device, feature_vdevice_mapping in self.abs_setup_feature_vdevice_mappings.items():
|
|
535
|
+
if cur_setup_device not in self._original_active_vdevice_mappings.keys():
|
|
536
|
+
self._original_active_vdevice_mappings[cur_setup_device] = FeatureVDeviceMapping()
|
|
537
|
+
for cur_setup_feature in feature_vdevice_mapping.features:
|
|
538
|
+
vdev_dev_mappings_of_setup_feat = feature_vdevice_mapping.get_mappings_for_feature(cur_setup_feature)
|
|
539
|
+
|
|
540
|
+
cur_setup_feature_vdevice = vdev_dev_mappings_of_setup_feat[0].vdevice
|
|
541
|
+
cur_mapped_setup_device = vdev_dev_mappings_of_setup_feat[0].device
|
|
542
|
+
|
|
543
|
+
# first save old value to revert it later
|
|
544
|
+
self._original_active_vdevice_mappings[cur_setup_device].add(
|
|
545
|
+
feature=cur_setup_feature,
|
|
546
|
+
mappings=cur_setup_feature.active_vdevices
|
|
547
|
+
)
|
|
548
|
+
# now set new value
|
|
549
|
+
cur_setup_feature.active_vdevices = {cur_setup_feature_vdevice: cur_mapped_setup_device}
|
|
550
|
+
|
|
551
|
+
# now also determine the mapping for the scenario-feature (if there exists one)
|
|
552
|
+
cur_scenario_device = self.get_scenario_device_for(cur_setup_device)
|
|
553
|
+
cur_scenario_feature = self.feature_replacement[cur_scenario_device].get_replaced_scenario_feature_for(
|
|
554
|
+
cur_setup_feature
|
|
555
|
+
)
|
|
556
|
+
if cur_scenario_feature is None:
|
|
557
|
+
# there exists no scenario feature -> we can ignore this
|
|
558
|
+
pass
|
|
559
|
+
else:
|
|
560
|
+
cur_scenario_feature_controller = FeatureController.get_for(cur_scenario_feature.__class__)
|
|
561
|
+
# now get the same vdevice (same name) but on scenario level
|
|
562
|
+
cur_scenario_feature_vdevice = [
|
|
563
|
+
cur_vdevice for cur_vdevice in cur_scenario_feature_controller.get_abs_inner_vdevice_classes()
|
|
564
|
+
if cur_vdevice.__name__ == cur_setup_feature_vdevice.__name__]
|
|
565
|
+
if len(cur_scenario_feature_vdevice) == 1:
|
|
566
|
+
# only if there exists exactly one scenario vdevice with the same name
|
|
567
|
+
|
|
568
|
+
if cur_scenario_device not in self._original_active_vdevice_mappings.keys():
|
|
569
|
+
self._original_active_vdevice_mappings[cur_scenario_device] = FeatureVDeviceMapping()
|
|
570
|
+
# first save old value to revert it later
|
|
571
|
+
self._original_active_vdevice_mappings[cur_scenario_device].add(
|
|
572
|
+
feature=cur_scenario_feature,
|
|
573
|
+
mappings=cur_scenario_feature.active_vdevices
|
|
574
|
+
)
|
|
575
|
+
# now set new value
|
|
576
|
+
cur_scenario_feature.active_vdevices = \
|
|
577
|
+
{cur_scenario_feature_vdevice[0]: self.get_scenario_device_for(cur_mapped_setup_device)}
|
|
578
|
+
|
|
579
|
+
def revert_active_vdevice_device_mappings_in_all_features(self):
|
|
580
|
+
"""
|
|
581
|
+
This method ensures that the `active_vdevices` property that was changed with
|
|
582
|
+
`update_active_vdevice_device_mappings_in_all_features()` will be reverted correctly.
|
|
583
|
+
"""
|
|
584
|
+
for cur_feature_vdevice_mapping in self._original_active_vdevice_mappings.values():
|
|
585
|
+
for cur_feature in cur_feature_vdevice_mapping.features:
|
|
586
|
+
cur_original_mapping = {
|
|
587
|
+
mapping.vdevice:mapping.device
|
|
588
|
+
for mapping in cur_feature_vdevice_mapping.get_mappings_for_feature(cur_feature)
|
|
589
|
+
}
|
|
590
|
+
cur_feature.active_vdevices = cur_original_mapping
|
|
591
|
+
|
|
592
|
+
def exchange_unmapped_vdevice_references(self):
|
|
593
|
+
"""
|
|
594
|
+
This method exchanges all :class:`VDevice` references to an instance of :class:`UnmappedVDevice` if the
|
|
595
|
+
:class:`VDevice` is not in an active VDevice-mapping.
|
|
596
|
+
"""
|
|
597
|
+
all_devices = NormalScenarioSetupController.get_for(
|
|
598
|
+
self.cur_scenario_class.__class__).get_all_abs_inner_device_classes()
|
|
599
|
+
all_devices += NormalScenarioSetupController.get_for(
|
|
600
|
+
self.cur_setup_class.__class__).get_all_abs_inner_device_classes()
|
|
601
|
+
|
|
602
|
+
for cur_device in all_devices:
|
|
603
|
+
cur_device_controller = DeviceController.get_for(cur_device)
|
|
604
|
+
for _, cur_feature in cur_device_controller.get_all_instantiated_feature_objects().items():
|
|
605
|
+
for cur_vdevice in FeatureController.get_for(cur_feature.__class__).get_abs_inner_vdevice_classes():
|
|
606
|
+
if cur_vdevice in cur_feature.active_vdevices.keys():
|
|
607
|
+
# do not exchange something, because this one is the active one
|
|
608
|
+
pass
|
|
609
|
+
else:
|
|
610
|
+
# set the `UnmappedVDevice` to this VDevice to ensure that it will not be accessed during this
|
|
611
|
+
# variation
|
|
612
|
+
setattr(cur_feature, cur_vdevice.__name__, UnmappedVDevice())
|
|
613
|
+
|
|
614
|
+
def restore_original_vdevice_references(self):
|
|
615
|
+
"""
|
|
616
|
+
This method restores all previously exchanged :class:`VDevice` references to the original ones.
|
|
617
|
+
"""
|
|
618
|
+
all_devices = NormalScenarioSetupController.get_for(
|
|
619
|
+
self.cur_scenario_class.__class__).get_all_abs_inner_device_classes()
|
|
620
|
+
all_devices += NormalScenarioSetupController.get_for(
|
|
621
|
+
self.cur_setup_class.__class__).get_all_abs_inner_device_classes()
|
|
622
|
+
|
|
623
|
+
for cur_device in all_devices:
|
|
624
|
+
cur_device_controller = DeviceController.get_for(cur_device)
|
|
625
|
+
for _, cur_feature in cur_device_controller.get_all_instantiated_feature_objects().items():
|
|
626
|
+
original_vdevices = FeatureController.get_for(cur_feature.__class__).get_original_vdevice_definitions()
|
|
627
|
+
for cur_vdevice_name, cur_original_vdevice in original_vdevices.items():
|
|
628
|
+
setattr(cur_feature, cur_vdevice_name, cur_original_vdevice)
|
|
629
|
+
|
|
630
|
+
def update_vdevice_referenced_feature_instances(self):
|
|
631
|
+
"""
|
|
632
|
+
This method ensures that all referenced feature instances in every active vDevice classes of the used feature
|
|
633
|
+
objects, will be replaced with the related feature instances of the mapped device object.
|
|
634
|
+
|
|
635
|
+
.. note::
|
|
636
|
+
Note that this method expects that the true defined scenario features are already replaced with the real
|
|
637
|
+
setup features. In addition to that, the method expects, that the vDevice-Device mapping of every feature
|
|
638
|
+
was set to the resolved setup device! So the method requires that the method
|
|
639
|
+
`update_scenario_device_feature_instances()` was called before.
|
|
640
|
+
"""
|
|
641
|
+
for scenario_device, _ in self._base_device_mapping.items():
|
|
642
|
+
# these features are subclasses of the real defined one (because they are already the replaced ones)
|
|
643
|
+
all_device_features = DeviceController.get_for(scenario_device).get_all_instantiated_feature_objects()
|
|
644
|
+
for _, cur_feature in all_device_features.items():
|
|
645
|
+
# now get the related vDevice class and update its attributes
|
|
646
|
+
cur_vdevice, cur_device = cur_feature.active_vdevice_device_mapping
|
|
647
|
+
if cur_vdevice is not None and cur_device is not None:
|
|
648
|
+
cur_vdevice_controller = VDeviceController.get_for(cur_vdevice)
|
|
649
|
+
cur_vdevice_all_features = cur_vdevice_controller.get_all_instantiated_feature_objects()
|
|
650
|
+
|
|
651
|
+
cur_device_controller = DeviceController.get_for(cur_device)
|
|
652
|
+
cur_device_all_features = cur_device_controller.get_all_instantiated_feature_objects()
|
|
653
|
+
for cur_vdevice_attr_name, cur_vdevice_feature in cur_vdevice_all_features.items():
|
|
654
|
+
# now search the used feature in the mapped device itself
|
|
655
|
+
potential_candidates = [
|
|
656
|
+
candidate_feature for _, candidate_feature in cur_device_all_features.items()
|
|
657
|
+
if isinstance(candidate_feature, cur_vdevice_feature.__class__)]
|
|
658
|
+
if len(potential_candidates) != 1:
|
|
659
|
+
raise RuntimeError("found none or more than one potential replacing candidates")
|
|
660
|
+
replacing_candidate = potential_candidates[0]
|
|
661
|
+
# because `cur_feature` is only the object instance, the value will be overwritten only for this
|
|
662
|
+
# object
|
|
663
|
+
setattr(cur_vdevice, cur_vdevice_attr_name, replacing_candidate)
|
|
664
|
+
|
|
665
|
+
def verify_applicability(self) -> None:
|
|
666
|
+
"""
|
|
667
|
+
This method verifies if this variation is executable. First the method checks if all defined
|
|
668
|
+
:class:`Feature` instances are available and fully implemented in the mapped setup :class:`Device`.
|
|
669
|
+
Furthermore, it checks if their exists a valid routing which also matches the defined class `@for_vdevice`
|
|
670
|
+
definition of the used :class:`Feature` classes.
|
|
671
|
+
"""
|
|
672
|
+
self._applicability_check_done = True
|
|
673
|
+
try:
|
|
674
|
+
self.determine_feature_replacement_and_vdevice_mappings()
|
|
675
|
+
|
|
676
|
+
self._verify_applicability_trough_feature_implementation_matching()
|
|
677
|
+
|
|
678
|
+
self._verify_applicability_trough_vdevice_feature_impl_matching()
|
|
679
|
+
|
|
680
|
+
self._verify_applicability_trough_all_valid_routings()
|
|
681
|
+
except NotApplicableVariationException as not_applicable_variation_exc:
|
|
682
|
+
# this variation can not be used, because the features can not be resolved correctly!
|
|
683
|
+
self._not_applicable_variation_exc = not_applicable_variation_exc
|
|
684
|
+
self.prev_mark = PreviousExecutorMark.DISCARDED
|
|
685
|
+
|
|
686
|
+
def can_be_applied(self) -> bool:
|
|
687
|
+
"""
|
|
688
|
+
:return: returns True if the previous verify_applicability check was successfully
|
|
689
|
+
"""
|
|
690
|
+
if self._applicability_check_done is not True:
|
|
691
|
+
raise RuntimeError('this method can not be used before no check was executed')
|
|
692
|
+
|
|
693
|
+
return self._not_applicable_variation_exc is None
|
|
694
|
+
|
|
695
|
+
def determine_absolute_scenario_device_connections(self):
|
|
696
|
+
"""
|
|
697
|
+
This method determines the absolute connections for this variation and sets the internal properties
|
|
698
|
+
`_abs_variation_*_device_connections` with them. This will be used to determine the real connection-subtree
|
|
699
|
+
(that can be used for this variation) by the method `create_all_valid_routings()`.
|
|
700
|
+
|
|
701
|
+
The method re-executes the algorithm to determine the absolute connections for a scenario/setup (see the method
|
|
702
|
+
:meth:`Collector.determine_absolute_device_connections_for`), but it considers the real applied vDevice and
|
|
703
|
+
their feature restrictions too.
|
|
704
|
+
"""
|
|
705
|
+
# first determine all relevant absolute connection depending on the current scenario
|
|
706
|
+
abs_var_scenario_device_cnns = self.cur_scenario_controller.get_all_abs_connections()
|
|
707
|
+
|
|
708
|
+
# now iterate over every feature, that is used by the scenario and determine the class-based feature connections
|
|
709
|
+
# of the mapped scenario feature (and its vDevice)
|
|
710
|
+
for cur_setup_device, feature_vdev_mapping in self.abs_setup_feature_vdevice_mappings.items():
|
|
711
|
+
cur_scenario_device = self.get_scenario_device_for(cur_setup_device)
|
|
712
|
+
for cur_setup_feature, vdev_mappings_of_setup_feature in feature_vdev_mapping.items():
|
|
713
|
+
cur_scenario_feature: Feature = (
|
|
714
|
+
self.feature_replacement[cur_scenario_device].get_replaced_scenario_feature_for(
|
|
715
|
+
setup_feature=cur_setup_feature)
|
|
716
|
+
)
|
|
717
|
+
|
|
718
|
+
if vdev_mappings_of_setup_feature[0].device not in self.base_device_mapping.values():
|
|
719
|
+
raise NotApplicableVariationException(
|
|
720
|
+
f'the mapped setup device `{vdev_mappings_of_setup_feature[0].device.__qualname__}` which is '
|
|
721
|
+
f'mapped to the VDevice `{vdev_mappings_of_setup_feature[0].vdevice.__qualname__}` is no part '
|
|
722
|
+
f'of this variation')
|
|
723
|
+
|
|
724
|
+
cur_mapped_scenario_device = self.get_scenario_device_for(vdev_mappings_of_setup_feature[0].device)
|
|
725
|
+
|
|
726
|
+
# get relevant class based connections for the current feature on setup level (this is really be used
|
|
727
|
+
# here)
|
|
728
|
+
feat_cnn = FeatureController.get_for(cur_setup_feature.__class__)\
|
|
729
|
+
.get_abs_class_based_for_vdevice()[vdev_mappings_of_setup_feature[0].vdevice]
|
|
730
|
+
# connection that are relevant for this feature
|
|
731
|
+
relevant_cnns = [
|
|
732
|
+
cnn for cnn in abs_var_scenario_device_cnns
|
|
733
|
+
if (cnn.has_connection_from_to(cur_scenario_device, end_device=cur_mapped_scenario_device)
|
|
734
|
+
and max(single_feat_cnn.contained_in(cnn, ignore_metadata=True)
|
|
735
|
+
for single_feat_cnn in feat_cnn.get_singles())
|
|
736
|
+
)
|
|
737
|
+
]
|
|
738
|
+
|
|
739
|
+
if len(relevant_cnns) > 1:
|
|
740
|
+
raise UnclearAssignableFeatureConnectionError(
|
|
741
|
+
f"the devices {cur_scenario_device.__name__} and {cur_mapped_scenario_device.__name__} have "
|
|
742
|
+
f"multiple parallel connections - the device `{cur_scenario_device.__name__}` uses a feature "
|
|
743
|
+
f"`{cur_scenario_feature.__class__.__name__}` that matches with the device "
|
|
744
|
+
f"`{cur_mapped_scenario_device.__name__}`, but it is not clear which of the parallel "
|
|
745
|
+
f"connection could be used")
|
|
746
|
+
|
|
747
|
+
if len(relevant_cnns) == 0:
|
|
748
|
+
# todo this does not map here
|
|
749
|
+
raise ValueError("can not find matching connection on scenario level")
|
|
750
|
+
|
|
751
|
+
relevant_device_cnn = relevant_cnns[0]
|
|
752
|
+
|
|
753
|
+
# now cleanup the scenario-device connection `relevant_device_cnn` according to the class-based feature
|
|
754
|
+
# connection
|
|
755
|
+
new_cnn_to_replace = Connection.based_on(OrConnectionRelation(*[
|
|
756
|
+
cur_old_cnn_single for cur_old_cnn_single in relevant_device_cnn.get_singles()
|
|
757
|
+
if feat_cnn.contained_in(cur_old_cnn_single, ignore_metadata=True)
|
|
758
|
+
]))
|
|
759
|
+
new_cnn_to_replace.set_metadata_for_all_subitems(relevant_device_cnn.metadata)
|
|
760
|
+
|
|
761
|
+
abs_var_scenario_device_cnns.remove(relevant_device_cnn)
|
|
762
|
+
abs_var_scenario_device_cnns.append(new_cnn_to_replace)
|
|
763
|
+
|
|
764
|
+
# we do not need to check other direction because `has_connection_from_to()` returns both possibilities
|
|
765
|
+
|
|
766
|
+
# set the determined values in variation object
|
|
767
|
+
self._abs_variation_scenario_device_connections = abs_var_scenario_device_cnns
|
|
768
|
+
|
|
769
|
+
def create_all_valid_routings(self):
|
|
770
|
+
"""
|
|
771
|
+
This method determines all valid routings for the current variation. It iterates over every defined
|
|
772
|
+
absolute determined :class:`Connection` in the :class:`Scenario` class and trys to find a valid routing for it.
|
|
773
|
+
|
|
774
|
+
.. note::
|
|
775
|
+
The method assigns a full report with all valid routings into the local property `_routings`.
|
|
776
|
+
"""
|
|
777
|
+
self._routings = {}
|
|
778
|
+
for scenario_device, _ in self._base_device_mapping.items():
|
|
779
|
+
for cur_scenario_conn in self._abs_variation_scenario_device_connections:
|
|
780
|
+
if cur_scenario_conn.has_connection_from_to(scenario_device):
|
|
781
|
+
# only check if the connection object has not yet been investigated
|
|
782
|
+
if cur_scenario_conn not in self._routings.keys():
|
|
783
|
+
# try to find a routing for the current connection
|
|
784
|
+
founded_routings = RoutingPath.route_through(
|
|
785
|
+
cur_scenario_conn, self._base_device_mapping)
|
|
786
|
+
self._routings[cur_scenario_conn] = founded_routings
|
|
787
|
+
|
|
788
|
+
def determine_abs_variation_connections(self):
|
|
789
|
+
"""
|
|
790
|
+
This method determines the absolute variation connections, that will be saved in the property
|
|
791
|
+
:meth:`VariationExecutor._abs_variation_connections`. This is the INTERSECTION between the active absolute
|
|
792
|
+
scenario connection for this variation (see
|
|
793
|
+
:meth:`VariationExecutor._abs_variation_scenario_device_connections`) and the virtual possible connections of
|
|
794
|
+
the possible routings (multiple routings will be combined over an OR relation).
|
|
795
|
+
|
|
796
|
+
This determined connection can directly be used to determine active method variations.
|
|
797
|
+
"""
|
|
798
|
+
virtual_routing_cnns = {}
|
|
799
|
+
for cur_cnn, cur_routing_list in self._routings.items():
|
|
800
|
+
virtual_routing_cnns[cur_cnn] = None
|
|
801
|
+
for cur_routing in cur_routing_list:
|
|
802
|
+
virtual_cnn = cur_routing.get_virtual_connection()
|
|
803
|
+
|
|
804
|
+
if virtual_routing_cnns[cur_cnn] is None:
|
|
805
|
+
virtual_routing_cnns[cur_cnn] = Connection.based_on(virtual_cnn)
|
|
806
|
+
else:
|
|
807
|
+
virtual_routing_cnns[cur_cnn] = Connection.based_on(
|
|
808
|
+
OrConnectionRelation(virtual_routing_cnns[cur_cnn], virtual_cnn))
|
|
809
|
+
virtual_routing_cnns[cur_cnn].set_metadata_for_all_subitems(virtual_cnn.metadata)
|
|
810
|
+
|
|
811
|
+
self._abs_variation_connections = []
|
|
812
|
+
for cur_cnn in self._abs_variation_scenario_device_connections:
|
|
813
|
+
cur_virtual_cnn = virtual_routing_cnns[cur_cnn]
|
|
814
|
+
new_intersection = cur_cnn.intersection_with(cur_virtual_cnn)
|
|
815
|
+
if new_intersection:
|
|
816
|
+
new_intersection.set_metadata_for_all_subitems(None)
|
|
817
|
+
# always set the metadata for setup devices
|
|
818
|
+
new_intersection.set_metadata_for_all_subitems(cur_virtual_cnn.metadata)
|
|
819
|
+
self._abs_variation_connections.append(new_intersection)
|
|
820
|
+
|
|
821
|
+
def set_conn_dependent_methods(self):
|
|
822
|
+
"""
|
|
823
|
+
This method sets a clear method instance for all method variations.
|
|
824
|
+
|
|
825
|
+
.. note::
|
|
826
|
+
It is important to call the method :meth:`VariationExecutor.determine_abs_variation_connections` and
|
|
827
|
+
the method :meth:`VariationExecutor.update_scenario_device_feature_instances` before!
|
|
828
|
+
"""
|
|
829
|
+
for _, setup_device in self._base_device_mapping.items():
|
|
830
|
+
setup_device_instantiated_features = \
|
|
831
|
+
DeviceController.get_for(setup_device).get_all_instantiated_feature_objects()
|
|
832
|
+
for _, cur_setup_feature in setup_device_instantiated_features.items():
|
|
833
|
+
|
|
834
|
+
cur_setup_feature_controller = FeatureController.get_for(cur_setup_feature.__class__)
|
|
835
|
+
method_var_data_of_feature = cur_setup_feature_controller.get_method_based_for_vdevice()
|
|
836
|
+
if method_var_data_of_feature is None:
|
|
837
|
+
# ignore if no method-variations exists for the current feature
|
|
838
|
+
continue
|
|
839
|
+
|
|
840
|
+
method_var_selection = {}
|
|
841
|
+
|
|
842
|
+
for cur_method_name, _ in inspect.getmembers(
|
|
843
|
+
cur_setup_feature, lambda o: inspect.isfunction(o) or inspect.ismethod(o)):
|
|
844
|
+
|
|
845
|
+
if cur_method_name not in method_var_data_of_feature.keys():
|
|
846
|
+
# ignore if there was no method-variation registration for the current method
|
|
847
|
+
continue
|
|
848
|
+
|
|
849
|
+
mapped_vdevice, mapped_setup_device = cur_setup_feature.active_vdevice_device_mapping
|
|
850
|
+
# get all absolute connections for this setup device
|
|
851
|
+
relevant_abs_conn = []
|
|
852
|
+
for cur_cnn in self._abs_variation_connections:
|
|
853
|
+
if cur_cnn.has_connection_from_to(start_device=setup_device, end_device=mapped_setup_device):
|
|
854
|
+
# add the children
|
|
855
|
+
relevant_abs_conn.extend(
|
|
856
|
+
cur_cnn.based_on_elements.connections if cur_cnn.__class__ == Connection else [cur_cnn]
|
|
857
|
+
)
|
|
858
|
+
|
|
859
|
+
if len(relevant_abs_conn) is None:
|
|
860
|
+
raise RuntimeError(f"detect empty absolute connection between device `{setup_device.__name__}` "
|
|
861
|
+
f"and device `{mapped_setup_device.__name__}`")
|
|
862
|
+
absolute_feature_method_var_cnn = Connection.based_on(OrConnectionRelation(*relevant_abs_conn))
|
|
863
|
+
cur_method_variation = cur_setup_feature_controller.get_method_variation(
|
|
864
|
+
of_method_name=cur_method_name, for_vdevice=mapped_vdevice,
|
|
865
|
+
with_connection=absolute_feature_method_var_cnn)
|
|
866
|
+
if cur_method_variation is None:
|
|
867
|
+
raise AttributeError(f"can not find a valid method variation for method `{cur_method_name}` "
|
|
868
|
+
f"of feature `{cur_setup_feature.__class__.__name__}`")
|
|
869
|
+
method_var_selection[cur_method_name] = \
|
|
870
|
+
(mapped_vdevice, absolute_feature_method_var_cnn, cur_method_variation)
|
|
871
|
+
|
|
872
|
+
cur_setup_feature_controller.set_active_method_variation(method_selection=method_var_selection)
|
|
873
|
+
|
|
874
|
+
def resolve_and_exchange_unresolved_parametrization(self):
|
|
875
|
+
"""resolves the parametrization if there are any :class:`UnresolvedParametrizedTestcaseExecutor` in the tree"""
|
|
876
|
+
replaced_executors = []
|
|
877
|
+
for cur_child in self._testcase_executors:
|
|
878
|
+
if isinstance(cur_child, UnresolvedParametrizedTestcaseExecutor):
|
|
879
|
+
replaced_executors.extend(cur_child.get_resolved_parametrized_testcase_executors())
|
|
880
|
+
else:
|
|
881
|
+
replaced_executors.append(cur_child)
|
|
882
|
+
self._testcase_executors = replaced_executors
|