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.
Files changed (89) hide show
  1. _balder/__init__.py +12 -0
  2. _balder/_version.py +34 -0
  3. _balder/balder_plugin.py +73 -0
  4. _balder/balder_session.py +341 -0
  5. _balder/balder_settings.py +15 -0
  6. _balder/cnnrelations/__init__.py +7 -0
  7. _balder/cnnrelations/and_connection_relation.py +176 -0
  8. _balder/cnnrelations/base_connection_relation.py +270 -0
  9. _balder/cnnrelations/or_connection_relation.py +65 -0
  10. _balder/collector.py +874 -0
  11. _balder/connection.py +863 -0
  12. _balder/connection_metadata.py +255 -0
  13. _balder/console/__init__.py +0 -0
  14. _balder/console/balder.py +58 -0
  15. _balder/controllers/__init__.py +12 -0
  16. _balder/controllers/base_device_controller.py +72 -0
  17. _balder/controllers/controller.py +29 -0
  18. _balder/controllers/device_controller.py +446 -0
  19. _balder/controllers/feature_controller.py +715 -0
  20. _balder/controllers/normal_scenario_setup_controller.py +402 -0
  21. _balder/controllers/scenario_controller.py +524 -0
  22. _balder/controllers/setup_controller.py +134 -0
  23. _balder/controllers/vdevice_controller.py +95 -0
  24. _balder/decorator_connect.py +104 -0
  25. _balder/decorator_covered_by.py +74 -0
  26. _balder/decorator_fixture.py +29 -0
  27. _balder/decorator_for_vdevice.py +118 -0
  28. _balder/decorator_gateway.py +34 -0
  29. _balder/decorator_insert_into_tree.py +52 -0
  30. _balder/decorator_parametrize.py +31 -0
  31. _balder/decorator_parametrize_by_feature.py +36 -0
  32. _balder/device.py +18 -0
  33. _balder/exceptions.py +182 -0
  34. _balder/executor/__init__.py +0 -0
  35. _balder/executor/basic_executable_executor.py +133 -0
  36. _balder/executor/basic_executor.py +205 -0
  37. _balder/executor/executor_tree.py +217 -0
  38. _balder/executor/parametrized_testcase_executor.py +52 -0
  39. _balder/executor/scenario_executor.py +169 -0
  40. _balder/executor/setup_executor.py +163 -0
  41. _balder/executor/testcase_executor.py +203 -0
  42. _balder/executor/unresolved_parametrized_testcase_executor.py +184 -0
  43. _balder/executor/variation_executor.py +882 -0
  44. _balder/exit_code.py +19 -0
  45. _balder/feature.py +74 -0
  46. _balder/feature_replacement_mapping.py +107 -0
  47. _balder/feature_vdevice_mapping.py +88 -0
  48. _balder/fixture_definition_scope.py +19 -0
  49. _balder/fixture_execution_level.py +22 -0
  50. _balder/fixture_manager.py +483 -0
  51. _balder/fixture_metadata.py +26 -0
  52. _balder/node_gateway.py +103 -0
  53. _balder/objects/__init__.py +0 -0
  54. _balder/objects/connections/__init__.py +0 -0
  55. _balder/objects/connections/osi_1_physical.py +116 -0
  56. _balder/objects/connections/osi_2_datalink.py +35 -0
  57. _balder/objects/connections/osi_3_network.py +47 -0
  58. _balder/objects/connections/osi_4_transport.py +40 -0
  59. _balder/objects/connections/osi_5_session.py +13 -0
  60. _balder/objects/connections/osi_6_presentation.py +13 -0
  61. _balder/objects/connections/osi_7_application.py +83 -0
  62. _balder/objects/devices/__init__.py +0 -0
  63. _balder/objects/devices/this_device.py +12 -0
  64. _balder/parametrization.py +75 -0
  65. _balder/plugin_manager.py +138 -0
  66. _balder/previous_executor_mark.py +23 -0
  67. _balder/routing_path.py +335 -0
  68. _balder/scenario.py +20 -0
  69. _balder/setup.py +18 -0
  70. _balder/solver.py +246 -0
  71. _balder/testresult.py +163 -0
  72. _balder/unmapped_vdevice.py +13 -0
  73. _balder/utils/__init__.py +0 -0
  74. _balder/utils/functions.py +103 -0
  75. _balder/utils/inner_device_managing_metaclass.py +14 -0
  76. _balder/utils/mixin_can_be_covered_by_executor.py +24 -0
  77. _balder/utils/typings.py +4 -0
  78. _balder/vdevice.py +9 -0
  79. balder/__init__.py +56 -0
  80. balder/connections.py +43 -0
  81. balder/devices.py +9 -0
  82. balder/exceptions.py +44 -0
  83. balder/parametrization.py +8 -0
  84. baldertest-0.1.0.dist-info/METADATA +356 -0
  85. baldertest-0.1.0.dist-info/RECORD +89 -0
  86. baldertest-0.1.0.dist-info/WHEEL +5 -0
  87. baldertest-0.1.0.dist-info/entry_points.txt +2 -0
  88. baldertest-0.1.0.dist-info/licenses/LICENSE +21 -0
  89. 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