baldertest 0.1.0b11__py3-none-any.whl → 0.1.0b13__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 (41) hide show
  1. _balder/_version.py +8 -3
  2. _balder/cnnrelations/and_connection_relation.py +28 -1
  3. _balder/cnnrelations/base_connection_relation.py +1 -1
  4. _balder/collector.py +101 -113
  5. _balder/connection.py +76 -80
  6. _balder/controllers/device_controller.py +17 -29
  7. _balder/controllers/feature_controller.py +2 -2
  8. _balder/controllers/normal_scenario_setup_controller.py +1 -2
  9. _balder/controllers/scenario_controller.py +121 -0
  10. _balder/controllers/vdevice_controller.py +2 -23
  11. _balder/decorator_connect.py +3 -3
  12. _balder/decorator_covered_by.py +28 -36
  13. _balder/executor/basic_executable_executor.py +13 -6
  14. _balder/executor/basic_executor.py +31 -4
  15. _balder/executor/executor_tree.py +5 -4
  16. _balder/executor/parametrized_testcase_executor.py +2 -2
  17. _balder/executor/scenario_executor.py +12 -71
  18. _balder/executor/setup_executor.py +4 -3
  19. _balder/executor/testcase_executor.py +35 -19
  20. _balder/executor/unresolved_parametrized_testcase_executor.py +32 -57
  21. _balder/executor/variation_executor.py +127 -148
  22. _balder/feature.py +2 -1
  23. _balder/feature_replacement_mapping.py +107 -0
  24. _balder/feature_vdevice_mapping.py +88 -0
  25. _balder/fixture_manager.py +1 -1
  26. _balder/fixture_metadata.py +1 -1
  27. _balder/routing_path.py +27 -16
  28. _balder/scenario.py +2 -2
  29. _balder/setup.py +2 -1
  30. _balder/testresult.py +4 -3
  31. _balder/utils/__init__.py +0 -0
  32. _balder/{utils.py → utils/functions.py} +29 -31
  33. _balder/utils/inner_device_managing_metaclass.py +14 -0
  34. _balder/utils/mixin_can_be_covered_by_executor.py +24 -0
  35. _balder/utils/typings.py +4 -0
  36. {baldertest-0.1.0b11.dist-info → baldertest-0.1.0b13.dist-info}/METADATA +3 -2
  37. {baldertest-0.1.0b11.dist-info → baldertest-0.1.0b13.dist-info}/RECORD +41 -35
  38. {baldertest-0.1.0b11.dist-info → baldertest-0.1.0b13.dist-info}/WHEEL +1 -1
  39. {baldertest-0.1.0b11.dist-info → baldertest-0.1.0b13.dist-info}/entry_points.txt +0 -0
  40. {baldertest-0.1.0b11.dist-info → baldertest-0.1.0b13.dist-info/licenses}/LICENSE +0 -0
  41. {baldertest-0.1.0b11.dist-info → baldertest-0.1.0b13.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,13 @@
1
1
  from __future__ import annotations
2
2
 
3
- import itertools
4
- from typing import Type, Union, List, Dict, Tuple, TYPE_CHECKING
3
+ from typing import Type, Union, List, Dict, TYPE_CHECKING
5
4
 
6
5
  import inspect
7
6
  import logging
8
7
  from _balder.cnnrelations import OrConnectionRelation
9
8
  from _balder.device import Device
10
9
  from _balder.connection import Connection
10
+ from _balder.feature_replacement_mapping import FeatureReplacementMapping
11
11
  from _balder.fixture_execution_level import FixtureExecutionLevel
12
12
  from _balder.testresult import ResultState, BranchBodyResult, ResultSummary
13
13
  from _balder.executor.basic_executable_executor import BasicExecutableExecutor
@@ -16,6 +16,7 @@ from _balder.executor.unresolved_parametrized_testcase_executor import Unresolve
16
16
  from _balder.previous_executor_mark import PreviousExecutorMark
17
17
  from _balder.routing_path import RoutingPath
18
18
  from _balder.unmapped_vdevice import UnmappedVDevice
19
+ from _balder.feature_vdevice_mapping import FeatureVDeviceMapping
19
20
  from _balder.controllers import DeviceController, VDeviceController, FeatureController, NormalScenarioSetupController
20
21
  from _balder.exceptions import NotApplicableVariationException, UnclearAssignableFeatureConnectionError
21
22
 
@@ -23,7 +24,6 @@ if TYPE_CHECKING:
23
24
  from _balder.setup import Setup
24
25
  from _balder.feature import Feature
25
26
  from _balder.scenario import Scenario
26
- from _balder.vdevice import VDevice
27
27
  from _balder.controllers.scenario_controller import ScenarioController
28
28
  from _balder.controllers.setup_controller import SetupController
29
29
  from _balder.executor.scenario_executor import ScenarioExecutor
@@ -50,12 +50,10 @@ class VariationExecutor(BasicExecutableExecutor):
50
50
  self._routings: Dict[Connection, List[RoutingPath]] = {}
51
51
  # buffer variable to save the feature replacement after it was determined with
52
52
  # `determine_feature_replacement_and_vdevice_mappings()`
53
- self._feature_replacement: \
54
- Union[None, Dict[Type[Device], Dict[str, Tuple[Union[Feature, None], Feature]]]] = None
53
+ self._feature_replacement: Union[None, Dict[Type[Device], FeatureReplacementMapping]] = None
55
54
  # buffer variable to save the feature replacement after it was determined with
56
55
  # `determine_feature_replacement_and_vdevice_mappings()`
57
- self._abs_setup_feature_vdevice_mappings: \
58
- Union[None, Dict[Type[Device], Dict[Feature, Dict[Type[VDevice], Type[Device]]]]] = None
56
+ self._abs_setup_feature_vdevice_mappings: Union[None, Dict[Type[Device], FeatureVDeviceMapping]] = None
59
57
 
60
58
  # contains the absolute scenario device connections for the current variation
61
59
  self._abs_variation_scenario_device_connections: Union[List[Connection], None] = None
@@ -67,8 +65,7 @@ class VariationExecutor(BasicExecutableExecutor):
67
65
  # contains the original active vdevice mappings for all scenario and setup devices (will be managed by
68
66
  # `update_active_vdevice_device_mappings_in_scenario_and_setup_devices()` and
69
67
  # `revert_active_vdevice_device_mappings_in_scenario_and_setup_devices()`)
70
- self._original_active_vdevice_mappings: \
71
- Dict[Type[Device], Dict[Feature, Dict[Type[VDevice], Type[Device]]]] = {}
68
+ self._original_active_vdevice_mappings: Dict[Type[Device], FeatureVDeviceMapping] = {}
72
69
 
73
70
  # is True if the applicability check was done
74
71
  self._applicability_check_done = False
@@ -138,18 +135,17 @@ class VariationExecutor(BasicExecutableExecutor):
138
135
  return self._fixture_manager
139
136
 
140
137
  @property
141
- def feature_replacement(self) -> Dict[Type[Device], Dict[str, Tuple[Union[Feature, None], Feature]]]:
138
+ def feature_replacement(self) -> Dict[Type[Device], FeatureReplacementMapping]:
142
139
  """
143
- this property is a dictionary with every scenario device as key and a dictionary as value - the value dictionary
144
- contains a tuple (inner dict value) for every attribute name(inner dict key) - the tuples always consist of two
145
- elements, the old feature as first item of the tuple (the instantiated feature from the scenario if it exists,
146
- otherwise this is None) and the new feature as second item (the feature of the related Setup-Device)
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)
147
144
  """
148
145
  return self._feature_replacement
149
146
 
150
147
  @property
151
- def abs_setup_feature_vdevice_mappings(self) \
152
- -> Dict[Type[Device], Dict[Feature, Dict[Type[VDevice], Type[Device]]]]:
148
+ def abs_setup_feature_vdevice_mappings(self) -> Dict[Type[Device], FeatureVDeviceMapping]:
153
149
  """returns the feature replacement that was determined with
154
150
  `determine_feature_replacement_and_vdevice_mappings()`"""
155
151
  return self._abs_setup_feature_vdevice_mappings
@@ -175,7 +171,7 @@ class VariationExecutor(BasicExecutableExecutor):
175
171
  self.exchange_unmapped_vdevice_references()
176
172
  self.update_vdevice_referenced_feature_instances()
177
173
  self.set_conn_dependent_methods()
178
- self.resolve_possible_parametrization()
174
+ self.resolve_and_exchange_unresolved_parametrization()
179
175
 
180
176
  def _body_execution(self, show_discarded):
181
177
  if show_discarded and not self.can_be_applied():
@@ -183,13 +179,10 @@ class VariationExecutor(BasicExecutableExecutor):
183
179
  return
184
180
 
185
181
  for cur_testcase_executor in self.get_testcase_executors():
186
- if cur_testcase_executor.has_runnable_tests():
187
-
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()):
188
185
  cur_testcase_executor.execute()
189
- elif cur_testcase_executor.prev_mark == PreviousExecutorMark.SKIP:
190
- cur_testcase_executor.set_result_for_whole_branch(ResultState.SKIP)
191
- elif cur_testcase_executor.prev_mark == PreviousExecutorMark.COVERED_BY:
192
- cur_testcase_executor.set_result_for_whole_branch(ResultState.COVERED_BY)
193
186
  else:
194
187
  cur_testcase_executor.set_result_for_whole_branch(ResultState.NOT_RUN)
195
188
 
@@ -230,26 +223,22 @@ class VariationExecutor(BasicExecutableExecutor):
230
223
  vDevices-Mappings (so the mapped setup-devices) implements all features that are defined in the vDevices
231
224
  """
232
225
 
233
- for cur_scenario_device, cur_replacement_dict in self.feature_replacement.items():
226
+ for cur_scenario_device, cur_replacement_mapping in self.feature_replacement.items():
234
227
  cur_setup_device = self.get_setup_device_for(scenario_device=cur_scenario_device)
235
228
  all_inner_setup_features = \
236
229
  DeviceController.get_for(cur_setup_device).get_all_instantiated_feature_objects()
237
230
 
238
- # describes the mapping from the new setup feature (key) to the instantiated scenario feature (value)
239
- # note that this dictionary only contains the required one
240
- setup_to_scenario_feature_mapping: Dict[Type[Feature], Feature] = {
241
- cur_replacement_tuple[1]: cur_replacement_tuple[0]
242
- for cur_attr_name, cur_replacement_tuple in cur_replacement_dict.items()
243
- if cur_replacement_tuple[0] is not None}
244
-
245
231
  # now secure that all features are available in the corresponding setup device, that are defined in the
246
232
  # mapped vDevice
247
233
  for _, cur_setup_feature_obj in all_inner_setup_features.items():
248
- # only check if there is a requirement of this feature (the feature is required by the Scenario)
249
- if cur_setup_feature_obj.__class__ not in setup_to_scenario_feature_mapping.keys():
250
- # ignore this, because no requirement for this feature
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
251
240
  continue
252
- related_scenario_feature_obj = setup_to_scenario_feature_mapping[cur_setup_feature_obj.__class__]
241
+
253
242
  # get vDevice and device mapping
254
243
  partner_scenario_vdevice, partner_scenario_device = \
255
244
  related_scenario_feature_obj.active_vdevice_device_mapping
@@ -258,7 +247,7 @@ class VariationExecutor(BasicExecutableExecutor):
258
247
  # ignore because no mapping exist here
259
248
  continue
260
249
 
261
- partner_setup_device = self.get_setup_device_for(scenario_device=partner_scenario_vdevice)
250
+ partner_setup_device = self.get_setup_device_for(scenario_device=partner_scenario_device)
262
251
  # get the related vDevice on setup view that is currently active
263
252
  mapped_setup_vdevices = [
264
253
  cur_vdevice for cur_vdevice
@@ -343,16 +332,9 @@ class VariationExecutor(BasicExecutableExecutor):
343
332
  return super().testsummary()
344
333
  return ResultSummary()
345
334
 
346
- def get_testcase_executors(self) -> List[TestcaseExecutor]:
335
+ def get_testcase_executors(self) -> List[TestcaseExecutor | UnresolvedParametrizedTestcaseExecutor]:
347
336
  """returns all sub testcase executors that belongs to this variation-executor"""
348
- result = []
349
- for cur_executor in self._testcase_executors:
350
- if (isinstance(cur_executor, UnresolvedParametrizedTestcaseExecutor) and
351
- cur_executor.parametrization_has_been_resolved):
352
- result += cur_executor.get_testcase_executors()
353
- else:
354
- result.append(cur_executor)
355
- return result
337
+ return self._testcase_executors.copy()
356
338
 
357
339
  def add_testcase_executor(self, testcase_executor: TestcaseExecutor | UnresolvedParametrizedTestcaseExecutor):
358
340
  """
@@ -388,15 +370,17 @@ class VariationExecutor(BasicExecutableExecutor):
388
370
  :raises NotApplicableVariationError: will be thrown if this variation cannot be applied, because the setup-/
389
371
  scenario-device-features can not be resolved
390
372
  """
391
- feature_replacement = {scenario_dev: {} for scenario_dev in self.base_device_mapping.keys()}
392
- abs_setup_vdevice_mappings = {setup_dev: {} for setup_dev in self.base_device_mapping.values()}
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
+ }
393
380
  for cur_scenario_device, cur_setup_device in self.base_device_mapping.items():
394
- cur_setup_features = DeviceController.get_for(cur_setup_device).get_all_instantiated_feature_objects()
395
381
 
396
- all_assigned_setup_features = []
397
- cur_scenario_device_orig_features = \
398
- DeviceController.get_for(cur_scenario_device).get_original_instanced_feature_objects()
399
- for cur_attr_name, cur_scenario_feature_obj in cur_scenario_device_orig_features.items():
382
+ for cur_attr_name, cur_scenario_feature_obj in \
383
+ DeviceController.get_for(cur_scenario_device).get_all_instantiated_feature_objects().items():
400
384
  active_scenario_vdevice, mapped_scenario_device = cur_scenario_feature_obj.active_vdevice_device_mapping
401
385
 
402
386
  cur_setup_feature_objs = self._get_matching_setup_features_for(
@@ -410,13 +394,14 @@ class VariationExecutor(BasicExecutableExecutor):
410
394
  f'`{cur_scenario_device.__name__}`) in setup device `{cur_setup_device.__name__}`')
411
395
  cur_setup_feature_obj = cur_setup_feature_objs[0]
412
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
+
413
401
  if mapped_scenario_device is None:
414
402
  # we have exactly one matching candidate, but also no vDevice mapping
415
403
  # check if the matching candidate has a vDevice mapping
416
- _, mapped_setup_device = cur_setup_feature_obj.active_vdevice_device_mapping
417
- cleanup_feature_controller = FeatureController.get_for(cur_setup_feature_obj.__class__)
418
- if mapped_setup_device is None \
419
- and len(cleanup_feature_controller.get_abs_inner_vdevice_classes()) > 0:
404
+ if mapped_setup_device is None and len(all_abs_inner_vdevs_of_setup) > 0:
420
405
  # there is no vDevice mapping on scenario and no vDevice mapping on setup level, but the
421
406
  # feature defined vDevices -> NOT APPLICABLE
422
407
  logger.warning(
@@ -430,42 +415,38 @@ class VariationExecutor(BasicExecutableExecutor):
430
415
  f'`{cur_scenario_feature_obj.__class__.__name__}` (used by scenario device '
431
416
  f'`{cur_scenario_device.__name__}`) in setup device `{cur_setup_device.__name__}`')
432
417
 
433
- all_assigned_setup_features.append(cur_setup_feature_obj)
434
- if cur_attr_name not in feature_replacement[cur_scenario_device].keys():
435
- cleanup_feature_controller = FeatureController.get_for(cur_setup_feature_obj.__class__)
436
-
437
- used_setup_vdevice, mapped_setup_device = cur_setup_feature_obj.active_vdevice_device_mapping
418
+ if cur_attr_name not in feature_replacement[cur_scenario_device].attr_names:
438
419
 
439
420
  # if there is a vDevice mapping on scenario level, but not on setup level, so update the
440
421
  # VDevice-Device-Mapping there
441
422
  if mapped_scenario_device is not None and mapped_setup_device is None:
442
423
  # search the equivalent vDevice on setup level and use this one (we had not to check it,
443
424
  # because check was already done in collector-stage)
444
- setup_vdevices = [cur_vdevice for cur_vdevice
445
- in cleanup_feature_controller.get_abs_inner_vdevice_classes()
425
+ setup_vdevices = [cur_vdevice for cur_vdevice in all_abs_inner_vdevs_of_setup
446
426
  if cur_vdevice.__name__ == active_scenario_vdevice.__name__]
447
427
  used_setup_vdevice = setup_vdevices[0]
448
428
  # set the mapping
449
- abs_setup_vdevice_mappings[cur_setup_device][cur_setup_feature_obj] = {
450
- used_setup_vdevice: self.get_setup_device_for(mapped_scenario_device)}
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
+ )
451
435
  # if there is a vDevice mapping on setup level, but not on scenario level, so directly update the
452
436
  # VDevice-Device-Mapping there
453
437
  elif mapped_scenario_device is None and mapped_setup_device is not None:
454
- abs_setup_vdevice_mappings[cur_setup_device][cur_setup_feature_obj] = {
455
- used_setup_vdevice: mapped_setup_device}
456
-
457
- feature_replacement[cur_scenario_device][cur_attr_name] = \
458
- (cur_scenario_feature_obj, cur_setup_feature_obj)
459
- # also add all setup features that are not assigned as autonomous features
460
- for cur_setup_feature in cur_setup_features.values():
461
- if cur_setup_feature not in all_assigned_setup_features:
462
- # determine free name
463
- idx = 0
464
- autonomous_name = None
465
- while autonomous_name is None or autonomous_name in feature_replacement[cur_scenario_device].keys():
466
- autonomous_name = f"_autonomous_feat_{idx}"
467
- idx += 1
468
- feature_replacement[cur_scenario_device][autonomous_name] = (None, cur_setup_feature)
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)
469
450
 
470
451
  # set the result to internal properties
471
452
  self._feature_replacement = feature_replacement
@@ -531,20 +512,18 @@ class VariationExecutor(BasicExecutableExecutor):
531
512
  This method ensures that the (mostly abstract) feature instances of a scenario are exchanged with the
532
513
  feature instances of the assigned setup devices
533
514
  """
534
- for cur_scenario_device, cur_replacement_dict in self.feature_replacement.items():
535
- for cur_attr_name, cur_replacement_tuple in cur_replacement_dict.items():
536
- _, new_feature_obj = cur_replacement_tuple
537
- setattr(cur_scenario_device, cur_attr_name, new_feature_obj)
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)
538
518
 
539
519
  def revert_scenario_device_feature_instances(self):
540
520
  """
541
521
  This method ensures that all initialized feature instances of a scenario are set back to the initial given
542
522
  features.
543
523
  """
544
- for cur_scenario_device, cur_replacement_dict in self.feature_replacement.items():
545
- for cur_attr_name, cur_replacement_tuple in cur_replacement_dict.items():
546
- old_instantiated_feature_obj, _ = cur_replacement_tuple
547
- setattr(cur_scenario_device, cur_attr_name, old_instantiated_feature_obj)
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)
548
527
 
549
528
  def update_active_vdevice_device_mappings_in_all_features(self):
550
529
  """
@@ -552,28 +531,28 @@ class VariationExecutor(BasicExecutableExecutor):
552
531
  scenario-device classes are set correctly.
553
532
  """
554
533
 
555
- for cur_setup_device, feature_dict in self.abs_setup_feature_vdevice_mappings.items():
534
+ for cur_setup_device, feature_vdevice_mapping in self.abs_setup_feature_vdevice_mappings.items():
556
535
  if cur_setup_device not in self._original_active_vdevice_mappings.keys():
557
- self._original_active_vdevice_mappings[cur_setup_device] = {}
558
- for cur_setup_feature, mapping_dict in feature_dict.items():
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)
559
539
 
560
- cur_setup_feature_vdevice = list(mapping_dict.keys())[0]
561
- cur_mapped_setup_device = list(mapping_dict.values())[0]
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
562
542
 
563
543
  # first save old value to revert it later
564
- self._original_active_vdevice_mappings[cur_setup_device][cur_setup_feature] = \
565
- cur_setup_feature.active_vdevices
544
+ self._original_active_vdevice_mappings[cur_setup_device].add(
545
+ feature=cur_setup_feature,
546
+ mappings=cur_setup_feature.active_vdevices
547
+ )
566
548
  # now set new value
567
549
  cur_setup_feature.active_vdevices = {cur_setup_feature_vdevice: cur_mapped_setup_device}
568
550
 
569
551
  # now also determine the mapping for the scenario-feature (if there exists one)
570
552
  cur_scenario_device = self.get_scenario_device_for(cur_setup_device)
571
- cur_scenario_feature = None
572
- for cur_replacement_scenario_feature, cur_replacement_setup_feature in \
573
- self.feature_replacement[cur_scenario_device].values():
574
- if cur_replacement_setup_feature == cur_setup_feature:
575
- cur_scenario_feature = cur_replacement_scenario_feature
576
- break
553
+ cur_scenario_feature = self.feature_replacement[cur_scenario_device].get_replaced_scenario_feature_for(
554
+ cur_setup_feature
555
+ )
577
556
  if cur_scenario_feature is None:
578
557
  # there exists no scenario feature -> we can ignore this
579
558
  pass
@@ -587,10 +566,12 @@ class VariationExecutor(BasicExecutableExecutor):
587
566
  # only if there exists exactly one scenario vdevice with the same name
588
567
 
589
568
  if cur_scenario_device not in self._original_active_vdevice_mappings.keys():
590
- self._original_active_vdevice_mappings[cur_scenario_device] = {}
569
+ self._original_active_vdevice_mappings[cur_scenario_device] = FeatureVDeviceMapping()
591
570
  # first save old value to revert it later
592
- self._original_active_vdevice_mappings[cur_scenario_device][cur_scenario_feature] = \
593
- cur_scenario_feature.active_vdevices
571
+ self._original_active_vdevice_mappings[cur_scenario_device].add(
572
+ feature=cur_scenario_feature,
573
+ mappings=cur_scenario_feature.active_vdevices
574
+ )
594
575
  # now set new value
595
576
  cur_scenario_feature.active_vdevices = \
596
577
  {cur_scenario_feature_vdevice[0]: self.get_scenario_device_for(cur_mapped_setup_device)}
@@ -598,10 +579,14 @@ class VariationExecutor(BasicExecutableExecutor):
598
579
  def revert_active_vdevice_device_mappings_in_all_features(self):
599
580
  """
600
581
  This method ensures that the `active_vdevices` property that was changed with
601
- `update_active_vdevice_device_mappings()` will be reverted correctly.
582
+ `update_active_vdevice_device_mappings_in_all_features()` will be reverted correctly.
602
583
  """
603
- for _, cur_feature_mapping_dict in self._original_active_vdevice_mappings.items():
604
- for cur_feature, cur_original_mapping in cur_feature_mapping_dict.items():
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
+ }
605
590
  cur_feature.active_vdevices = cur_original_mapping
606
591
 
607
592
  def exchange_unmapped_vdevice_references(self):
@@ -661,7 +646,7 @@ class VariationExecutor(BasicExecutableExecutor):
661
646
  cur_vdevice, cur_device = cur_feature.active_vdevice_device_mapping
662
647
  if cur_vdevice is not None and cur_device is not None:
663
648
  cur_vdevice_controller = VDeviceController.get_for(cur_vdevice)
664
- cur_vdevice_all_features = cur_vdevice_controller.get_original_instanced_feature_objects()
649
+ cur_vdevice_all_features = cur_vdevice_controller.get_all_instantiated_feature_objects()
665
650
 
666
651
  cur_device_controller = DeviceController.get_for(cur_device)
667
652
  cur_device_all_features = cur_device_controller.get_all_instantiated_feature_objects()
@@ -722,64 +707,54 @@ class VariationExecutor(BasicExecutableExecutor):
722
707
 
723
708
  # now iterate over every feature, that is used by the scenario and determine the class-based feature connections
724
709
  # of the mapped scenario feature (and its vDevice)
725
- for cur_setup_device, feature_dict in self.abs_setup_feature_vdevice_mappings.items():
710
+ for cur_setup_device, feature_vdev_mapping in self.abs_setup_feature_vdevice_mappings.items():
726
711
  cur_scenario_device = self.get_scenario_device_for(cur_setup_device)
727
- for cur_setup_feature, mapping_dict in feature_dict.items():
728
- feature_replacement = {
729
- cur_tuple[1]: cur_tuple[0] for _, cur_tuple in self.feature_replacement[cur_scenario_device].items()
730
- }
731
- cur_scenario_feature: Feature = feature_replacement[cur_setup_feature]
732
- cur_setup_feature_vdevice = list(mapping_dict.keys())[0]
733
- cur_mapped_setup_device = list(mapping_dict.values())[0]
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
+ )
734
717
 
735
- if cur_mapped_setup_device not in self.base_device_mapping.values():
718
+ if vdev_mappings_of_setup_feature[0].device not in self.base_device_mapping.values():
736
719
  raise NotApplicableVariationException(
737
- f'the mapped setup device `{cur_mapped_setup_device.__qualname__}` which is mapped to the '
738
- f'VDevice `{cur_setup_feature_vdevice.__qualname__}` is no part of this variation')
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')
739
723
 
740
- cur_mapped_scenario_device = self.get_scenario_device_for(cur_mapped_setup_device)
724
+ cur_mapped_scenario_device = self.get_scenario_device_for(vdev_mappings_of_setup_feature[0].device)
741
725
 
742
726
  # get relevant class based connections for the current feature on setup level (this is really be used
743
727
  # here)
744
- feature_cnn = \
745
- FeatureController.get_for(
746
- cur_setup_feature.__class__).get_abs_class_based_for_vdevice()[cur_setup_feature_vdevice]
728
+ feat_cnn = FeatureController.get_for(cur_setup_feature.__class__)\
729
+ .get_abs_class_based_for_vdevice()[vdev_mappings_of_setup_feature[0].vdevice]
747
730
  # connection that are relevant for this feature
748
731
  relevant_cnns = [
749
732
  cnn for cnn in abs_var_scenario_device_cnns
750
- if cnn.has_connection_from_to(cur_scenario_device, end_device=cur_mapped_scenario_device)
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
+ )
751
737
  ]
752
738
 
753
- relevant_device_cnn = None
754
-
755
739
  if len(relevant_cnns) > 1:
756
- # we have parallel possibilities -> determine the selected one (only one is allowed to fit)
757
- for cur_relevant_cnn in relevant_cnns:
758
- for cur_relevant_single_cnn, cur_feature_single_cnn in (
759
- itertools.product(cur_relevant_cnn.get_singles(), feature_cnn.get_singles())):
760
- if not cur_feature_single_cnn.contained_in(cur_relevant_single_cnn, ignore_metadata=True):
761
- continue
762
- if relevant_device_cnn is not None:
763
- raise UnclearAssignableFeatureConnectionError(
764
- f"the devices {cur_scenario_device.__name__} and "
765
- f"{cur_mapped_scenario_device.__name__} have multiple parallel connections - the "
766
- f"device `{cur_scenario_device.__name__}` uses a feature "
767
- f"`{cur_scenario_feature.__class__.__name__}` that matches with the device "
768
- f"`{cur_mapped_scenario_device.__name__}`, but it is not clear which of the "
769
- f"parallel connection could be used"
770
- )
771
- relevant_device_cnn = cur_relevant_cnn
772
- elif len(relevant_cnns) == 1:
773
- relevant_device_cnn = relevant_cnns[0]
774
- if relevant_device_cnn is None:
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:
775
748
  # todo this does not map here
776
749
  raise ValueError("can not find matching connection on scenario level")
777
750
 
751
+ relevant_device_cnn = relevant_cnns[0]
752
+
778
753
  # now cleanup the scenario-device connection `relevant_device_cnn` according to the class-based feature
779
754
  # connection
780
755
  new_cnn_to_replace = Connection.based_on(OrConnectionRelation(*[
781
756
  cur_old_cnn_single for cur_old_cnn_single in relevant_device_cnn.get_singles()
782
- if feature_cnn.contained_in(cur_old_cnn_single, ignore_metadata=True)
757
+ if feat_cnn.contained_in(cur_old_cnn_single, ignore_metadata=True)
783
758
  ]))
784
759
  new_cnn_to_replace.set_metadata_for_all_subitems(relevant_device_cnn.metadata)
785
760
 
@@ -896,8 +871,12 @@ class VariationExecutor(BasicExecutableExecutor):
896
871
 
897
872
  cur_setup_feature_controller.set_active_method_variation(method_selection=method_var_selection)
898
873
 
899
- def resolve_possible_parametrization(self):
874
+ def resolve_and_exchange_unresolved_parametrization(self):
900
875
  """resolves the parametrization if there are any :class:`UnresolvedParametrizedTestcaseExecutor` in the tree"""
876
+ replaced_executors = []
901
877
  for cur_child in self._testcase_executors:
902
878
  if isinstance(cur_child, UnresolvedParametrizedTestcaseExecutor):
903
- cur_child.resolve_dynamic_parametrization()
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
_balder/feature.py CHANGED
@@ -3,9 +3,10 @@ from typing import Type, Dict, Tuple, Union
3
3
 
4
4
  from _balder.device import Device
5
5
  from _balder.vdevice import VDevice
6
+ from _balder.utils.inner_device_managing_metaclass import InnerDeviceManagingMetaclass
6
7
 
7
8
 
8
- class Feature:
9
+ class Feature(metaclass=InnerDeviceManagingMetaclass):
9
10
  """
10
11
  This is the basic feature class. It represents an abstract class that should not be used directly. It is the base
11
12
  class for all feature elements.
@@ -0,0 +1,107 @@
1
+ from typing import Union, Type
2
+ import dataclasses
3
+ from .device import Device
4
+ from .feature import Feature
5
+ from .controllers import DeviceController
6
+
7
+
8
+ class FeatureReplacementMapping:
9
+ """
10
+ helper object that stores mappings between scenario and setup features - is used in :class:`VariationExecutor`
11
+ """
12
+
13
+ @dataclasses.dataclass
14
+ class FeatureMapping:
15
+ """
16
+ stores a single mapping
17
+ """
18
+ #: the feature attribute name in scenario device
19
+ attr_name: str
20
+ #: the scenario feature instance or None if the current variation does not use this setup feature in scenario
21
+ scenario_feature: Union[Feature, None]
22
+ #: the setup feature that is used for the scenario feature
23
+ setup_feature: Feature
24
+
25
+ def __init__(self):
26
+ self._mappings: list[FeatureReplacementMapping.FeatureMapping] = []
27
+
28
+ @property
29
+ def mappings(self) -> list[FeatureMapping]:
30
+ """
31
+ returns all existing mappings
32
+ """
33
+ return list(self._mappings)
34
+
35
+ @property
36
+ def attr_names(self) -> list[str]:
37
+ """
38
+ returns all used attribute names
39
+ """
40
+ return [mapping.attr_name for mapping in self._mappings]
41
+
42
+ @property
43
+ def all_scenario_features(self) -> list[Feature]:
44
+ """
45
+ returns all scenario features
46
+ """
47
+ return [mapping.scenario_feature for mapping in self._mappings]
48
+
49
+ @property
50
+ def all_setup_features(self) -> list[Feature]:
51
+ """
52
+ returns all setup features
53
+ """
54
+ return [mapping.setup_feature for mapping in self._mappings]
55
+
56
+ def add(self, attr_name: str, scenario_feature: Union[Feature, None], setup_feature: Feature):
57
+ """
58
+ adds a new mapping
59
+
60
+ :param attr_name: the feature attribute name in scenario device
61
+ :param scenario_feature: the scenario feature instance or None if the current variation does not use this setup
62
+ feature in scenario
63
+ :param setup_feature: the setup feature that is used for the scenario feature
64
+ """
65
+ if attr_name in self.attr_names:
66
+ raise KeyError(f'entry for property name `{attr_name}` already exist - can not define it multiple times')
67
+ self._mappings.append(FeatureReplacementMapping.FeatureMapping(attr_name, scenario_feature, setup_feature))
68
+
69
+ def get_features_for_attr_name(self, attr_name: str) -> tuple[Feature, Feature]:
70
+ """
71
+ returns the scenario and its mapped setup feature for a specific attribute name used in the scenario device
72
+ """
73
+ for mapping in self._mappings:
74
+ if mapping.attr_name == attr_name:
75
+ return mapping.scenario_feature, mapping.setup_feature
76
+ raise KeyError(f'entry for property name `{attr_name}` does not exist')
77
+
78
+ def get_replaced_scenario_feature_for(self, setup_feature: Feature) -> Union[Feature, None]:
79
+ """
80
+ returns the mapped scenario feature for a given setup feature
81
+ """
82
+ for mapping in self._mappings:
83
+ if mapping.setup_feature == setup_feature:
84
+ return mapping.scenario_feature
85
+ raise KeyError(f'cannot find setup feature for {setup_feature}')
86
+
87
+ def add_remaining_setup_features_as_autonomous(self, cur_setup_device: Type[Device]):
88
+ """
89
+ Adds all features that were not added as autonomous features.
90
+ """
91
+ cur_setup_features = DeviceController.get_for(cur_setup_device).get_all_instantiated_feature_objects()
92
+
93
+ # also add all setup features that are not assigned as autonomous features
94
+ for cur_setup_feature in cur_setup_features.values():
95
+ if cur_setup_feature not in self.all_setup_features:
96
+ # determine free name
97
+ idx = 0
98
+ autonomous_name = None
99
+ while (autonomous_name is None
100
+ or autonomous_name in self.attr_names):
101
+ autonomous_name = f"_autonomous_feat_{idx}"
102
+ idx += 1
103
+ self.add(
104
+ attr_name=autonomous_name,
105
+ scenario_feature=None,
106
+ setup_feature=cur_setup_feature
107
+ )