baldertest 0.1.0b10__py3-none-any.whl → 0.1.0b12__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 (34) hide show
  1. _balder/_version.py +1 -1
  2. _balder/cnnrelations/__init__.py +7 -0
  3. _balder/cnnrelations/and_connection_relation.py +149 -0
  4. _balder/cnnrelations/base_connection_relation.py +270 -0
  5. _balder/cnnrelations/or_connection_relation.py +65 -0
  6. _balder/collector.py +10 -16
  7. _balder/connection.py +400 -881
  8. _balder/connection_metadata.py +255 -0
  9. _balder/controllers/device_controller.py +37 -16
  10. _balder/controllers/feature_controller.py +63 -99
  11. _balder/controllers/normal_scenario_setup_controller.py +5 -5
  12. _balder/controllers/scenario_controller.py +6 -6
  13. _balder/controllers/setup_controller.py +2 -3
  14. _balder/decorator_connect.py +12 -10
  15. _balder/decorator_for_vdevice.py +17 -25
  16. _balder/decorator_gateway.py +3 -3
  17. _balder/executor/testcase_executor.py +0 -1
  18. _balder/executor/variation_executor.py +212 -199
  19. _balder/feature.py +1 -1
  20. _balder/feature_replacement_mapping.py +69 -0
  21. _balder/feature_vdevice_mapping.py +88 -0
  22. _balder/fixture_manager.py +10 -9
  23. _balder/objects/connections/osi_3_network.py +2 -2
  24. _balder/objects/connections/osi_4_transport.py +2 -2
  25. _balder/routing_path.py +27 -31
  26. _balder/solver.py +1 -1
  27. _balder/testresult.py +1 -1
  28. _balder/utils.py +27 -1
  29. {baldertest-0.1.0b10.dist-info → baldertest-0.1.0b12.dist-info}/METADATA +2 -2
  30. {baldertest-0.1.0b10.dist-info → baldertest-0.1.0b12.dist-info}/RECORD +34 -27
  31. {baldertest-0.1.0b10.dist-info → baldertest-0.1.0b12.dist-info}/WHEEL +1 -1
  32. {baldertest-0.1.0b10.dist-info → baldertest-0.1.0b12.dist-info}/LICENSE +0 -0
  33. {baldertest-0.1.0b10.dist-info → baldertest-0.1.0b12.dist-info}/entry_points.txt +0 -0
  34. {baldertest-0.1.0b10.dist-info → baldertest-0.1.0b12.dist-info}/top_level.txt +0 -0
@@ -1,10 +1,13 @@
1
1
  from __future__ import annotations
2
- from typing import Type, Union, List, Dict, Tuple, TYPE_CHECKING
2
+
3
+ from typing import Type, Union, List, Dict, TYPE_CHECKING
3
4
 
4
5
  import inspect
5
6
  import logging
7
+ from _balder.cnnrelations import OrConnectionRelation
6
8
  from _balder.device import Device
7
9
  from _balder.connection import Connection
10
+ from _balder.feature_replacement_mapping import FeatureReplacementMapping
8
11
  from _balder.fixture_execution_level import FixtureExecutionLevel
9
12
  from _balder.testresult import ResultState, BranchBodyResult, ResultSummary
10
13
  from _balder.executor.basic_executable_executor import BasicExecutableExecutor
@@ -13,6 +16,7 @@ from _balder.executor.unresolved_parametrized_testcase_executor import Unresolve
13
16
  from _balder.previous_executor_mark import PreviousExecutorMark
14
17
  from _balder.routing_path import RoutingPath
15
18
  from _balder.unmapped_vdevice import UnmappedVDevice
19
+ from _balder.feature_vdevice_mapping import FeatureVDeviceMapping
16
20
  from _balder.controllers import DeviceController, VDeviceController, FeatureController, NormalScenarioSetupController
17
21
  from _balder.exceptions import NotApplicableVariationException, UnclearAssignableFeatureConnectionError
18
22
 
@@ -20,7 +24,8 @@ if TYPE_CHECKING:
20
24
  from _balder.setup import Setup
21
25
  from _balder.feature import Feature
22
26
  from _balder.scenario import Scenario
23
- from _balder.vdevice import VDevice
27
+ from _balder.controllers.scenario_controller import ScenarioController
28
+ from _balder.controllers.setup_controller import SetupController
24
29
  from _balder.executor.scenario_executor import ScenarioExecutor
25
30
  from _balder.fixture_manager import FixtureManager
26
31
 
@@ -45,12 +50,10 @@ class VariationExecutor(BasicExecutableExecutor):
45
50
  self._routings: Dict[Connection, List[RoutingPath]] = {}
46
51
  # buffer variable to save the feature replacement after it was determined with
47
52
  # `determine_feature_replacement_and_vdevice_mappings()`
48
- self._feature_replacement: \
49
- Union[None, Dict[Type[Device], Dict[str, Tuple[Union[Feature, None], Feature]]]] = None
53
+ self._feature_replacement: Union[None, Dict[Type[Device], FeatureReplacementMapping]] = None
50
54
  # buffer variable to save the feature replacement after it was determined with
51
55
  # `determine_feature_replacement_and_vdevice_mappings()`
52
- self._abs_setup_feature_vdevice_mappings: \
53
- 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
54
57
 
55
58
  # contains the absolute scenario device connections for the current variation
56
59
  self._abs_variation_scenario_device_connections: Union[List[Connection], None] = None
@@ -62,8 +65,7 @@ class VariationExecutor(BasicExecutableExecutor):
62
65
  # contains the original active vdevice mappings for all scenario and setup devices (will be managed by
63
66
  # `update_active_vdevice_device_mappings_in_scenario_and_setup_devices()` and
64
67
  # `revert_active_vdevice_device_mappings_in_scenario_and_setup_devices()`)
65
- self._original_active_vdevice_mappings: \
66
- Dict[Type[Device], Dict[Feature, Dict[Type[VDevice], Type[Device]]]] = {}
68
+ self._original_active_vdevice_mappings: Dict[Type[Device], FeatureVDeviceMapping] = {}
67
69
 
68
70
  # is True if the applicability check was done
69
71
  self._applicability_check_done = False
@@ -96,11 +98,25 @@ class VariationExecutor(BasicExecutableExecutor):
96
98
  """property returns the current :class:`Scenario` for this variation"""
97
99
  return self._parent_executor.base_scenario_class
98
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
+
99
108
  @property
100
109
  def cur_setup_class(self) -> Setup:
101
110
  """property returns the current :class:`Setup` for this variation"""
102
111
  return self._parent_executor.parent_executor.base_setup_class
103
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
+
104
120
  @property
105
121
  def base_device_mapping(self) -> Dict[Type[Device], Type[Device]]:
106
122
  """
@@ -119,18 +135,17 @@ class VariationExecutor(BasicExecutableExecutor):
119
135
  return self._fixture_manager
120
136
 
121
137
  @property
122
- def feature_replacement(self) -> Dict[Type[Device], Dict[str, Tuple[Union[Feature, None], Feature]]]:
138
+ def feature_replacement(self) -> Dict[Type[Device], FeatureReplacementMapping]:
123
139
  """
124
- this property is a dictionary with every scenario device as key and a dictionary as value - the value dictionary
125
- contains a tuple (inner dict value) for every attribute name(inner dict key) - the tuples always consist of two
126
- elements, the old feature as first item of the tuple (the instantiated feature from the scenario if it exists,
127
- 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)
128
144
  """
129
145
  return self._feature_replacement
130
146
 
131
147
  @property
132
- def abs_setup_feature_vdevice_mappings(self) \
133
- -> Dict[Type[Device], Dict[Feature, Dict[Type[VDevice], Type[Device]]]]:
148
+ def abs_setup_feature_vdevice_mappings(self) -> Dict[Type[Device], FeatureVDeviceMapping]:
134
149
  """returns the feature replacement that was determined with
135
150
  `determine_feature_replacement_and_vdevice_mappings()`"""
136
151
  return self._abs_setup_feature_vdevice_mappings
@@ -211,26 +226,22 @@ class VariationExecutor(BasicExecutableExecutor):
211
226
  vDevices-Mappings (so the mapped setup-devices) implements all features that are defined in the vDevices
212
227
  """
213
228
 
214
- for cur_scenario_device, cur_replacement_dict in self.feature_replacement.items():
229
+ for cur_scenario_device, cur_replacement_mapping in self.feature_replacement.items():
215
230
  cur_setup_device = self.get_setup_device_for(scenario_device=cur_scenario_device)
216
231
  all_inner_setup_features = \
217
232
  DeviceController.get_for(cur_setup_device).get_all_instantiated_feature_objects()
218
233
 
219
- # describes the mapping from the new setup feature (key) to the instantiated scenario feature (value)
220
- # note that this dictionary only contains the required one
221
- setup_to_scenario_feature_mapping: Dict[Type[Feature], Feature] = {
222
- cur_replacement_tuple[1]: cur_replacement_tuple[0]
223
- for cur_attr_name, cur_replacement_tuple in cur_replacement_dict.items()
224
- if cur_replacement_tuple[0] is not None}
225
-
226
234
  # now secure that all features are available in the corresponding setup device, that are defined in the
227
235
  # mapped vDevice
228
236
  for _, cur_setup_feature_obj in all_inner_setup_features.items():
229
- # only check if there is a requirement of this feature (the feature is required by the Scenario)
230
- if cur_setup_feature_obj.__class__ not in setup_to_scenario_feature_mapping.keys():
231
- # ignore this, because no requirement for this feature
237
+ related_scenario_feature_obj = \
238
+ cur_replacement_mapping.get_replaced_scenario_feature_for(cur_setup_feature_obj)
239
+
240
+ # only check if this feature is required by the scenario
241
+ if related_scenario_feature_obj is None:
242
+ # ignore this, because this feature is not used in the scenario
232
243
  continue
233
- related_scenario_feature_obj = setup_to_scenario_feature_mapping[cur_setup_feature_obj.__class__]
244
+
234
245
  # get vDevice and device mapping
235
246
  partner_scenario_vdevice, partner_scenario_device = \
236
247
  related_scenario_feature_obj.active_vdevice_device_mapping
@@ -239,7 +250,7 @@ class VariationExecutor(BasicExecutableExecutor):
239
250
  # ignore because no mapping exist here
240
251
  continue
241
252
 
242
- partner_setup_device = self.get_setup_device_for(scenario_device=partner_scenario_vdevice)
253
+ partner_setup_device = self.get_setup_device_for(scenario_device=partner_scenario_device)
243
254
  # get the related vDevice on setup view that is currently active
244
255
  mapped_setup_vdevices = [
245
256
  cur_vdevice for cur_vdevice
@@ -284,6 +295,39 @@ class VariationExecutor(BasicExecutableExecutor):
284
295
  f'can not find a valid routing on setup level for the connection `{scenario_cnn.get_tree_str()}` '
285
296
  f'between scenario devices `{scenario_cnn.from_device}` and `{scenario_cnn.to_device}`')
286
297
 
298
+ def _get_matching_setup_features_for(
299
+ self,
300
+ scenario_feature_obj: Feature,
301
+ in_setup_device: Type[Device]
302
+ ) -> List[Feature]:
303
+ """
304
+ Helper method that returns all matching setup features for the provided scenario feature in the provided setup
305
+ device.
306
+ """
307
+ cur_setup_features = DeviceController.get_for(in_setup_device).get_all_instantiated_feature_objects()
308
+
309
+ replacing_feature_candidates = [
310
+ cur_setup_feature for cur_setup_feature in cur_setup_features.values()
311
+ if isinstance(cur_setup_feature, scenario_feature_obj.__class__)
312
+ ]
313
+ active_scenario_vdev, mapped_scenario_dev = scenario_feature_obj.active_vdevice_device_mapping
314
+
315
+ replacing_features = replacing_feature_candidates.copy()
316
+ if mapped_scenario_dev is not None:
317
+ # get the related setup device for the mapped scenario device (on scenario level)
318
+ setup_dev_of_mapped_scenario_dev = self.get_setup_device_for(mapped_scenario_dev)
319
+
320
+ # now check if there is a mapping on setup level too
321
+ for cur_replacing_feature in replacing_feature_candidates:
322
+ mapped_setup_vdev, mapped_setup_dev = cur_replacing_feature.active_vdevice_device_mapping
323
+ if mapped_setup_vdev is not None and not issubclass(mapped_setup_vdev, active_scenario_vdev):
324
+ # drop this feature matching, because we have different vdevice mapped
325
+ replacing_features.remove(cur_replacing_feature)
326
+ elif mapped_setup_dev is not None and mapped_setup_dev != setup_dev_of_mapped_scenario_dev:
327
+ # drop this feature matching, because it is not applicable here
328
+ replacing_features.remove(cur_replacing_feature)
329
+ return replacing_features
330
+
287
331
  # ---------------------------------- METHODS -----------------------------------------------------------------------
288
332
 
289
333
  def testsummary(self) -> ResultSummary:
@@ -336,75 +380,58 @@ class VariationExecutor(BasicExecutableExecutor):
336
380
  :raises NotApplicableVariationError: will be thrown if this variation cannot be applied, because the setup-/
337
381
  scenario-device-features can not be resolved
338
382
  """
339
- feature_replacement = {}
340
- abs_setup_vdevice_mappings = {}
383
+ feature_replacement = {
384
+ scenario_dev: FeatureReplacementMapping() for scenario_dev in self.base_device_mapping.keys()
385
+ }
386
+
387
+ abs_setup_vdevice_mappings = {
388
+ setup_dev: FeatureVDeviceMapping() for setup_dev in self.base_device_mapping.values()
389
+ }
341
390
  for cur_scenario_device, cur_setup_device in self.base_device_mapping.items():
342
391
  cur_setup_features = DeviceController.get_for(cur_setup_device).get_all_instantiated_feature_objects()
343
392
 
344
- if cur_scenario_device not in feature_replacement.keys():
345
- feature_replacement[cur_scenario_device] = {}
393
+ all_assigned_setup_features = []
394
+ cur_scenario_device_features = \
395
+ DeviceController.get_for(cur_scenario_device).get_all_instantiated_feature_objects()
396
+ for cur_attr_name, cur_scenario_feature_obj in cur_scenario_device_features.items():
397
+ active_scenario_vdevice, mapped_scenario_device = cur_scenario_feature_obj.active_vdevice_device_mapping
346
398
 
347
- if cur_setup_device not in abs_setup_vdevice_mappings.keys():
348
- abs_setup_vdevice_mappings[cur_setup_device] = {}
399
+ cur_setup_feature_objs = self._get_matching_setup_features_for(
400
+ scenario_feature_obj=cur_scenario_feature_obj, in_setup_device=cur_setup_device
401
+ )
349
402
 
350
- all_assigned_setup_features = []
351
- cur_scenario_device_orig_features = \
352
- DeviceController.get_for(cur_scenario_device).get_original_instanced_feature_objects()
353
- for cur_attr_name, cur_abstract_scenario_feature_obj in cur_scenario_device_orig_features.items():
354
- replacing_features = [cur_setup_feature for _, cur_setup_feature in cur_setup_features.items()
355
- if isinstance(cur_setup_feature, cur_abstract_scenario_feature_obj.__class__)]
356
- used_scenario_vdevice, mapped_scenario_device = \
357
- cur_abstract_scenario_feature_obj.active_vdevice_device_mapping
358
- # check if there is a mapped device in scenario level
359
- cleanup_replacing_features = replacing_features.copy()
360
- if mapped_scenario_device is not None:
361
- # get the related setup device for the mapped scenario device (on scenario level)
362
- to_scenarios_vdevice_mapped_setup_device = self.get_setup_device_for(mapped_scenario_device)
363
-
364
- # now check if there is a mapping on setup level too
365
- for cur_replacing_feature in replacing_features:
366
- mapped_setup_vdevice, mapped_setup_device = cur_replacing_feature.active_vdevice_device_mapping
367
- if mapped_setup_vdevice is not None and \
368
- not issubclass(mapped_setup_vdevice, used_scenario_vdevice):
369
- # drop this feature matching, because we have different vdevice mapped
370
- cleanup_replacing_features.remove(cur_replacing_feature)
371
- elif mapped_setup_device is not None and \
372
- mapped_setup_device != to_scenarios_vdevice_mapped_setup_device:
373
- # drop this feature matching, because it is not applicable here
374
- cleanup_replacing_features.remove(cur_replacing_feature)
375
-
376
- if len(cleanup_replacing_features) != 1:
403
+ if len(cur_setup_feature_objs) != 1:
377
404
  raise NotApplicableVariationException(
378
405
  f'this variation can not be applicable because there was no setup feature implementation of '
379
- f'`{cur_abstract_scenario_feature_obj.__class__.__name__}` (used by scenario device '
406
+ f'`{cur_scenario_feature_obj.__class__.__name__}` (used by scenario device '
380
407
  f'`{cur_scenario_device.__name__}`) in setup device `{cur_setup_device.__name__}`')
408
+ cur_setup_feature_obj = cur_setup_feature_objs[0]
381
409
 
382
410
  if mapped_scenario_device is None:
383
411
  # we have exactly one matching candidate, but also no vDevice mapping
384
412
  # check if the matching candidate has a vDevice mapping
385
- _, mapped_setup_device = cleanup_replacing_features[0].active_vdevice_device_mapping
386
- cleanup_feature_controller = FeatureController.get_for(cleanup_replacing_features[0].__class__)
413
+ _, mapped_setup_device = cur_setup_feature_obj.active_vdevice_device_mapping
414
+ cleanup_feature_controller = FeatureController.get_for(cur_setup_feature_obj.__class__)
387
415
  if mapped_setup_device is None \
388
416
  and len(cleanup_feature_controller.get_abs_inner_vdevice_classes()) > 0:
389
417
  # there is no vDevice mapping on scenario and no vDevice mapping on setup level, but the
390
418
  # feature defined vDevices -> NOT APPLICABLE
391
419
  logger.warning(
392
420
  f"missing vDevice mapping for feature "
393
- f"`{cur_abstract_scenario_feature_obj.__class__.__name__}` (used in scenario device "
421
+ f"`{cur_scenario_feature_obj.__class__.__name__}` (used in scenario device "
394
422
  f"`{cur_scenario_device.__name__}` and in setup device `{cur_setup_device.__name__}`) - "
395
423
  f"VARIATION CAN NOT BE APPLIED")
396
424
  raise NotApplicableVariationException(
397
- f'this variation can not be applicable because there was no vDevice mapping given on '
425
+ f'this variation can not be applied because there was no vDevice mapping given on '
398
426
  f'scenario or on setup level for the feature '
399
- f'`{cur_abstract_scenario_feature_obj.__class__.__name__}` (used by scenario device '
427
+ f'`{cur_scenario_feature_obj.__class__.__name__}` (used by scenario device '
400
428
  f'`{cur_scenario_device.__name__}`) in setup device `{cur_setup_device.__name__}`')
401
429
 
402
- all_assigned_setup_features.append(cleanup_replacing_features[0])
403
- if cur_attr_name not in feature_replacement[cur_scenario_device].keys():
404
- cleanup_feature = cleanup_replacing_features[0]
405
- cleanup_feature_controller = FeatureController.get_for(cleanup_feature.__class__)
430
+ all_assigned_setup_features.append(cur_setup_feature_obj)
431
+ if cur_attr_name not in feature_replacement[cur_scenario_device].attr_names:
432
+ cleanup_feature_controller = FeatureController.get_for(cur_setup_feature_obj.__class__)
406
433
 
407
- used_setup_vdevice, mapped_setup_device = cleanup_feature.active_vdevice_device_mapping
434
+ used_setup_vdevice, mapped_setup_device = cur_setup_feature_obj.active_vdevice_device_mapping
408
435
 
409
436
  # if there is a vDevice mapping on scenario level, but not on setup level, so update the
410
437
  # VDevice-Device-Mapping there
@@ -413,29 +440,44 @@ class VariationExecutor(BasicExecutableExecutor):
413
440
  # because check was already done in collector-stage)
414
441
  setup_vdevices = [cur_vdevice for cur_vdevice
415
442
  in cleanup_feature_controller.get_abs_inner_vdevice_classes()
416
- if cur_vdevice.__name__ == used_scenario_vdevice.__name__]
443
+ if cur_vdevice.__name__ == active_scenario_vdevice.__name__]
417
444
  used_setup_vdevice = setup_vdevices[0]
418
445
  # set the mapping
419
- abs_setup_vdevice_mappings[cur_setup_device][cleanup_feature] = {
420
- used_setup_vdevice: self.get_setup_device_for(mapped_scenario_device)}
446
+ abs_setup_vdevice_mappings[cur_setup_device].add(
447
+ feature=cur_setup_feature_obj,
448
+ mappings={
449
+ used_setup_vdevice: self.get_setup_device_for(mapped_scenario_device)
450
+ }
451
+ )
421
452
  # if there is a vDevice mapping on setup level, but not on scenario level, so directly update the
422
453
  # VDevice-Device-Mapping there
423
454
  elif mapped_scenario_device is None and mapped_setup_device is not None:
424
- abs_setup_vdevice_mappings[cur_setup_device][cleanup_feature] = {
425
- used_setup_vdevice: mapped_setup_device}
455
+ abs_setup_vdevice_mappings[cur_setup_device].add(
456
+ feature=cur_setup_feature_obj,
457
+ mappings={
458
+ used_setup_vdevice: mapped_setup_device
459
+ }
460
+ )
461
+
462
+ feature_replacement[cur_scenario_device].add(attr_name=cur_attr_name,
463
+ scenario_feature=cur_scenario_feature_obj,
464
+ setup_feature=cur_setup_feature_obj)
426
465
 
427
- feature_replacement[cur_scenario_device][cur_attr_name] = \
428
- (cur_abstract_scenario_feature_obj, cleanup_feature)
429
466
  # also add all setup features that are not assigned as autonomous features
430
- for _, cur_setup_feature in cur_setup_features.items():
467
+ for cur_setup_feature in cur_setup_features.values():
431
468
  if cur_setup_feature not in all_assigned_setup_features:
432
469
  # determine free name
433
470
  idx = 0
434
471
  autonomous_name = None
435
- while autonomous_name is None or autonomous_name in feature_replacement[cur_scenario_device].keys():
472
+ while (autonomous_name is None
473
+ or autonomous_name in feature_replacement[cur_scenario_device].attr_names):
436
474
  autonomous_name = f"_autonomous_feat_{idx}"
437
475
  idx += 1
438
- feature_replacement[cur_scenario_device][autonomous_name] = (None, cur_setup_feature)
476
+ feature_replacement[cur_scenario_device].add(
477
+ attr_name=autonomous_name,
478
+ scenario_feature=None,
479
+ setup_feature=cur_setup_feature
480
+ )
439
481
 
440
482
  # set the result to internal properties
441
483
  self._feature_replacement = feature_replacement
@@ -501,20 +543,18 @@ class VariationExecutor(BasicExecutableExecutor):
501
543
  This method ensures that the (mostly abstract) feature instances of a scenario are exchanged with the
502
544
  feature instances of the assigned setup devices
503
545
  """
504
- for cur_scenario_device, cur_replacement_dict in self.feature_replacement.items():
505
- for cur_attr_name, cur_replacement_tuple in cur_replacement_dict.items():
506
- _, new_feature_obj = cur_replacement_tuple
507
- setattr(cur_scenario_device, cur_attr_name, new_feature_obj)
546
+ for cur_scenario_device, cur_replacement_mapping in self.feature_replacement.items():
547
+ for cur_feature_mapping in cur_replacement_mapping.mappings:
548
+ setattr(cur_scenario_device, cur_feature_mapping.attr_name, cur_feature_mapping.setup_feature)
508
549
 
509
550
  def revert_scenario_device_feature_instances(self):
510
551
  """
511
552
  This method ensures that all initialized feature instances of a scenario are set back to the initial given
512
553
  features.
513
554
  """
514
- for cur_scenario_device, cur_replacement_dict in self.feature_replacement.items():
515
- for cur_attr_name, cur_replacement_tuple in cur_replacement_dict.items():
516
- old_instantiated_feature_obj, _ = cur_replacement_tuple
517
- setattr(cur_scenario_device, cur_attr_name, old_instantiated_feature_obj)
555
+ for cur_scenario_device, cur_replacement_mapping in self.feature_replacement.items():
556
+ for cur_feature_mapping in cur_replacement_mapping.mappings:
557
+ setattr(cur_scenario_device, cur_feature_mapping.attr_name, cur_feature_mapping.scenario_feature)
518
558
 
519
559
  def update_active_vdevice_device_mappings_in_all_features(self):
520
560
  """
@@ -522,28 +562,28 @@ class VariationExecutor(BasicExecutableExecutor):
522
562
  scenario-device classes are set correctly.
523
563
  """
524
564
 
525
- for cur_setup_device, feature_dict in self.abs_setup_feature_vdevice_mappings.items():
565
+ for cur_setup_device, feature_vdevice_mapping in self.abs_setup_feature_vdevice_mappings.items():
526
566
  if cur_setup_device not in self._original_active_vdevice_mappings.keys():
527
- self._original_active_vdevice_mappings[cur_setup_device] = {}
528
- for cur_setup_feature, mapping_dict in feature_dict.items():
567
+ self._original_active_vdevice_mappings[cur_setup_device] = FeatureVDeviceMapping()
568
+ for cur_setup_feature in feature_vdevice_mapping.features:
569
+ vdev_dev_mappings_of_setup_feat = feature_vdevice_mapping.get_mappings_for_feature(cur_setup_feature)
529
570
 
530
- cur_setup_feature_vdevice = list(mapping_dict.keys())[0]
531
- cur_mapped_setup_device = list(mapping_dict.values())[0]
571
+ cur_setup_feature_vdevice = vdev_dev_mappings_of_setup_feat[0].vdevice
572
+ cur_mapped_setup_device = vdev_dev_mappings_of_setup_feat[0].device
532
573
 
533
574
  # first save old value to revert it later
534
- self._original_active_vdevice_mappings[cur_setup_device][cur_setup_feature] = \
535
- cur_setup_feature.active_vdevices
575
+ self._original_active_vdevice_mappings[cur_setup_device].add(
576
+ feature=cur_setup_feature,
577
+ mappings=cur_setup_feature.active_vdevices
578
+ )
536
579
  # now set new value
537
580
  cur_setup_feature.active_vdevices = {cur_setup_feature_vdevice: cur_mapped_setup_device}
538
581
 
539
582
  # now also determine the mapping for the scenario-feature (if there exists one)
540
583
  cur_scenario_device = self.get_scenario_device_for(cur_setup_device)
541
- cur_scenario_feature = None
542
- for cur_replacement_scenario_feature, cur_replacement_setup_feature in \
543
- self.feature_replacement[cur_scenario_device].values():
544
- if cur_replacement_setup_feature == cur_setup_feature:
545
- cur_scenario_feature = cur_replacement_scenario_feature
546
- break
584
+ cur_scenario_feature = self.feature_replacement[cur_scenario_device].get_replaced_scenario_feature_for(
585
+ cur_setup_feature
586
+ )
547
587
  if cur_scenario_feature is None:
548
588
  # there exists no scenario feature -> we can ignore this
549
589
  pass
@@ -557,10 +597,12 @@ class VariationExecutor(BasicExecutableExecutor):
557
597
  # only if there exists exactly one scenario vdevice with the same name
558
598
 
559
599
  if cur_scenario_device not in self._original_active_vdevice_mappings.keys():
560
- self._original_active_vdevice_mappings[cur_scenario_device] = {}
600
+ self._original_active_vdevice_mappings[cur_scenario_device] = FeatureVDeviceMapping()
561
601
  # first save old value to revert it later
562
- self._original_active_vdevice_mappings[cur_scenario_device][cur_scenario_feature] = \
563
- cur_scenario_feature.active_vdevices
602
+ self._original_active_vdevice_mappings[cur_scenario_device].add(
603
+ feature=cur_scenario_feature,
604
+ mappings=cur_scenario_feature.active_vdevices
605
+ )
564
606
  # now set new value
565
607
  cur_scenario_feature.active_vdevices = \
566
608
  {cur_scenario_feature_vdevice[0]: self.get_scenario_device_for(cur_mapped_setup_device)}
@@ -568,10 +610,14 @@ class VariationExecutor(BasicExecutableExecutor):
568
610
  def revert_active_vdevice_device_mappings_in_all_features(self):
569
611
  """
570
612
  This method ensures that the `active_vdevices` property that was changed with
571
- `update_active_vdevice_device_mappings()` will be reverted correctly.
613
+ `update_active_vdevice_device_mappings_in_all_features()` will be reverted correctly.
572
614
  """
573
- for _, cur_feature_mapping_dict in self._original_active_vdevice_mappings.items():
574
- for cur_feature, cur_original_mapping in cur_feature_mapping_dict.items():
615
+ for cur_feature_vdevice_mapping in self._original_active_vdevice_mappings.values():
616
+ for cur_feature in cur_feature_vdevice_mapping.features:
617
+ cur_original_mapping = {
618
+ mapping.vdevice:mapping.device
619
+ for mapping in cur_feature_vdevice_mapping.get_mappings_for_feature(cur_feature)
620
+ }
575
621
  cur_feature.active_vdevices = cur_original_mapping
576
622
 
577
623
  def exchange_unmapped_vdevice_references(self):
@@ -631,7 +677,7 @@ class VariationExecutor(BasicExecutableExecutor):
631
677
  cur_vdevice, cur_device = cur_feature.active_vdevice_device_mapping
632
678
  if cur_vdevice is not None and cur_device is not None:
633
679
  cur_vdevice_controller = VDeviceController.get_for(cur_vdevice)
634
- cur_vdevice_all_features = cur_vdevice_controller.get_original_instanced_feature_objects()
680
+ cur_vdevice_all_features = cur_vdevice_controller.get_all_instantiated_feature_objects()
635
681
 
636
682
  cur_device_controller = DeviceController.get_for(cur_device)
637
683
  cur_device_all_features = cur_device_controller.get_all_instantiated_feature_objects()
@@ -679,116 +725,77 @@ class VariationExecutor(BasicExecutableExecutor):
679
725
 
680
726
  def determine_absolute_scenario_device_connections(self):
681
727
  """
682
- This method determines the variation absolute connections for this variation and sets them to the internal
683
- properties `_abs_variation_*_device_connections`. This will be used to determine the real connection-subtree
728
+ This method determines the absolute connections for this variation and sets the internal properties
729
+ `_abs_variation_*_device_connections` with them. This will be used to determine the real connection-subtree
684
730
  (that can be used for this variation) by the method `create_all_valid_routings()`.
685
731
 
686
732
  The method re-executes the algorithm to determine the absolute connections for a scenario/setup (see the method
687
733
  :meth:`Collector.determine_absolute_device_connections_for`), but it considers the real applied vDevice and
688
734
  their feature restrictions too.
689
735
  """
690
- abs_var_scenario_device_cnns = {}
691
-
692
736
  # first determine all relevant absolute connection depending on the current scenario
693
- for cur_scenario_device, _ in self.base_device_mapping.items():
694
- cur_scenario_device_abs_cnns = \
695
- DeviceController.get_for(cur_scenario_device).get_all_absolute_connections()
696
- for _, cur_cnn_list in cur_scenario_device_abs_cnns.items():
697
- for cur_cnn in cur_cnn_list:
698
- if cur_scenario_device not in abs_var_scenario_device_cnns.keys():
699
- abs_var_scenario_device_cnns[cur_scenario_device] = {}
700
-
701
- cur_to_device, _ = cur_cnn.get_conn_partner_of(cur_scenario_device)
702
-
703
- if cur_to_device not in abs_var_scenario_device_cnns[cur_scenario_device].keys():
704
- abs_var_scenario_device_cnns[cur_scenario_device][cur_to_device] = []
705
-
706
- abs_var_scenario_device_cnns[cur_scenario_device][cur_to_device].append(cur_cnn)
737
+ abs_var_scenario_device_cnns = self.cur_scenario_controller.get_all_abs_connections()
707
738
 
708
739
  # now iterate over every feature, that is used by the scenario and determine the class-based feature connections
709
740
  # of the mapped scenario feature (and its vDevice)
710
- for cur_setup_device, feature_dict in self.abs_setup_feature_vdevice_mappings.items():
741
+ for cur_setup_device, feature_vdev_mapping in self.abs_setup_feature_vdevice_mappings.items():
711
742
  cur_scenario_device = self.get_scenario_device_for(cur_setup_device)
712
- for cur_setup_feature, mapping_dict in feature_dict.items():
713
- feature_replacement = {
714
- cur_tuple[1]: cur_tuple[0] for _, cur_tuple in self.feature_replacement[cur_scenario_device].items()
715
- }
716
- cur_scenario_feature: Feature = feature_replacement[cur_setup_feature]
717
- cur_setup_feature_vdevice = list(mapping_dict.keys())[0]
718
- cur_mapped_setup_device = list(mapping_dict.values())[0]
743
+ for cur_setup_feature, vdev_mappings_of_setup_feature in feature_vdev_mapping.items():
744
+ cur_scenario_feature: Feature = (
745
+ self.feature_replacement[cur_scenario_device].get_replaced_scenario_feature_for(
746
+ setup_feature=cur_setup_feature)
747
+ )
719
748
 
720
- if cur_mapped_setup_device not in self.base_device_mapping.values():
749
+ if vdev_mappings_of_setup_feature[0].device not in self.base_device_mapping.values():
721
750
  raise NotApplicableVariationException(
722
- f'the mapped setup device `{cur_mapped_setup_device.__qualname__}` which is mapped to the '
723
- f'VDevice `{cur_setup_feature_vdevice.__qualname__}` is no part of this variation')
751
+ f'the mapped setup device `{vdev_mappings_of_setup_feature[0].device.__qualname__}` which is '
752
+ f'mapped to the VDevice `{vdev_mappings_of_setup_feature[0].vdevice.__qualname__}` is no part '
753
+ f'of this variation')
724
754
 
725
- cur_mapped_scenario_device = self.get_scenario_device_for(cur_mapped_setup_device)
755
+ cur_mapped_scenario_device = self.get_scenario_device_for(vdev_mappings_of_setup_feature[0].device)
726
756
 
727
757
  # get relevant class based connections for the current feature on setup level (this is really be used
728
758
  # here)
729
- feature_cnns = \
730
- FeatureController.get_for(
731
- cur_setup_feature.__class__).get_abs_class_based_for_vdevice()[cur_setup_feature_vdevice]
759
+ feat_cnn = FeatureController.get_for(cur_setup_feature.__class__)\
760
+ .get_abs_class_based_for_vdevice()[vdev_mappings_of_setup_feature[0].vdevice]
732
761
  # connection that are relevant for this feature
733
- relevant_cnns = abs_var_scenario_device_cnns[cur_scenario_device][cur_mapped_scenario_device]
734
-
735
- relevant_device_cnn = None
762
+ relevant_cnns = [
763
+ cnn for cnn in abs_var_scenario_device_cnns
764
+ if (cnn.has_connection_from_to(cur_scenario_device, end_device=cur_mapped_scenario_device)
765
+ and max(single_feat_cnn.contained_in(cnn, ignore_metadata=True)
766
+ for single_feat_cnn in feat_cnn.get_singles())
767
+ )
768
+ ]
736
769
 
737
770
  if len(relevant_cnns) > 1:
738
- # we have parallel possibilities -> determine the selected one (only one is allowed to fit)
739
- for cur_relevant_cnn in relevant_cnns:
740
- for cur_relevant_single_cnn in cur_relevant_cnn.get_singles():
741
- for cur_feature_cnn in feature_cnns:
742
- for cur_feature_single_cnn in cur_feature_cnn.get_singles():
743
- if cur_feature_single_cnn.contained_in(cur_relevant_single_cnn):
744
- if relevant_device_cnn is not None:
745
- raise UnclearAssignableFeatureConnectionError(
746
- f"the devices {cur_scenario_device.__name__} and "
747
- f"{cur_mapped_scenario_device.__name__} have multiple parallel "
748
- f"connections - the device `{cur_scenario_device.__name__}` uses a "
749
- f"feature `{cur_scenario_feature.__class__.__name__}` that matches "
750
- f"with the device `{cur_mapped_scenario_device.__name__}`, but it is "
751
- f"not clear which of the parallel connection could be used"
752
- )
753
- relevant_device_cnn = cur_relevant_cnn
754
- elif len(relevant_cnns) == 1:
755
- relevant_device_cnn = relevant_cnns[0]
756
- if relevant_device_cnn is None:
771
+ raise UnclearAssignableFeatureConnectionError(
772
+ f"the devices {cur_scenario_device.__name__} and {cur_mapped_scenario_device.__name__} have "
773
+ f"multiple parallel connections - the device `{cur_scenario_device.__name__}` uses a feature "
774
+ f"`{cur_scenario_feature.__class__.__name__}` that matches with the device "
775
+ f"`{cur_mapped_scenario_device.__name__}`, but it is not clear which of the parallel "
776
+ f"connection could be used")
777
+
778
+ if len(relevant_cnns) == 0:
757
779
  # todo this does not map here
758
780
  raise ValueError("can not find matching connection on scenario level")
759
781
 
782
+ relevant_device_cnn = relevant_cnns[0]
783
+
760
784
  # now cleanup the scenario-device connection `relevant_device_cnn` according to the class-based feature
761
785
  # connection
762
- new_cleaned_singles = []
763
- for cur_old_cnn_single in relevant_device_cnn.get_singles():
764
- for cur_feature_cnn in feature_cnns:
765
- if cur_feature_cnn.contained_in(cur_old_cnn_single, ignore_metadata=True):
766
- new_cleaned_singles.append(cur_old_cnn_single)
767
-
768
- new_cnn_to_replace = Connection.based_on(*new_cleaned_singles)
769
- new_cnn_to_replace.set_metadata_for_all_subitems(new_cleaned_singles[0].metadata)
770
-
771
- abs_var_scenario_device_cnns[cur_scenario_device][cur_mapped_scenario_device].remove(
772
- relevant_device_cnn)
773
- abs_var_scenario_device_cnns[cur_scenario_device][cur_mapped_scenario_device].append(new_cnn_to_replace)
774
-
775
- # also search the connection in the other direction
776
- other_dir_relevant_device_cnn = None
777
- for cur_cnn in abs_var_scenario_device_cnns[cur_mapped_scenario_device][cur_scenario_device]:
778
- if cur_cnn.equal_with(relevant_device_cnn):
779
- other_dir_relevant_device_cnn = cur_cnn
780
- break
781
- # and also replace it
782
- abs_var_scenario_device_cnns[cur_mapped_scenario_device][cur_scenario_device].remove(
783
- other_dir_relevant_device_cnn)
784
- abs_var_scenario_device_cnns[cur_mapped_scenario_device][cur_scenario_device].append(new_cnn_to_replace)
786
+ new_cnn_to_replace = Connection.based_on(OrConnectionRelation(*[
787
+ cur_old_cnn_single for cur_old_cnn_single in relevant_device_cnn.get_singles()
788
+ if feat_cnn.contained_in(cur_old_cnn_single, ignore_metadata=True)
789
+ ]))
790
+ new_cnn_to_replace.set_metadata_for_all_subitems(relevant_device_cnn.metadata)
791
+
792
+ abs_var_scenario_device_cnns.remove(relevant_device_cnn)
793
+ abs_var_scenario_device_cnns.append(new_cnn_to_replace)
794
+
795
+ # we do not need to check other direction because `has_connection_from_to()` returns both possibilities
785
796
 
786
797
  # set the determined values in variation object
787
- self._abs_variation_scenario_device_connections = []
788
- for _, from_device_dict in abs_var_scenario_device_cnns.items():
789
- for _, cur_cnn_list in from_device_dict.items():
790
- for cur_cnn in cur_cnn_list:
791
- self._abs_variation_scenario_device_connections.append(cur_cnn)
798
+ self._abs_variation_scenario_device_connections = abs_var_scenario_device_cnns
792
799
 
793
800
  def create_all_valid_routings(self):
794
801
  """
@@ -828,16 +835,18 @@ class VariationExecutor(BasicExecutableExecutor):
828
835
  if virtual_routing_cnns[cur_cnn] is None:
829
836
  virtual_routing_cnns[cur_cnn] = Connection.based_on(virtual_cnn)
830
837
  else:
831
- virtual_routing_cnns[cur_cnn] = Connection.based_on(virtual_routing_cnns[cur_cnn], virtual_cnn)
838
+ virtual_routing_cnns[cur_cnn] = Connection.based_on(
839
+ OrConnectionRelation(virtual_routing_cnns[cur_cnn], virtual_cnn))
832
840
  virtual_routing_cnns[cur_cnn].set_metadata_for_all_subitems(virtual_cnn.metadata)
833
841
 
834
842
  self._abs_variation_connections = []
835
843
  for cur_cnn in self._abs_variation_scenario_device_connections:
836
844
  cur_virtual_cnn = virtual_routing_cnns[cur_cnn]
837
845
  new_intersection = cur_cnn.intersection_with(cur_virtual_cnn)
838
- new_intersection.set_metadata_for_all_subitems(None)
839
- # always set the metadata for setup devices
840
- new_intersection.set_metadata_for_all_subitems(cur_virtual_cnn.metadata)
846
+ if new_intersection:
847
+ new_intersection.set_metadata_for_all_subitems(None)
848
+ # always set the metadata for setup devices
849
+ new_intersection.set_metadata_for_all_subitems(cur_virtual_cnn.metadata)
841
850
  self._abs_variation_connections.append(new_intersection)
842
851
 
843
852
  def set_conn_dependent_methods(self):
@@ -873,11 +882,15 @@ class VariationExecutor(BasicExecutableExecutor):
873
882
  relevant_abs_conn = []
874
883
  for cur_cnn in self._abs_variation_connections:
875
884
  if cur_cnn.has_connection_from_to(start_device=setup_device, end_device=mapped_setup_device):
876
- relevant_abs_conn.append(cur_cnn)
885
+ # add the children
886
+ relevant_abs_conn.extend(
887
+ cur_cnn.based_on_elements.connections if cur_cnn.__class__ == Connection else [cur_cnn]
888
+ )
889
+
877
890
  if len(relevant_abs_conn) is None:
878
891
  raise RuntimeError(f"detect empty absolute connection between device `{setup_device.__name__}` "
879
892
  f"and device `{mapped_setup_device.__name__}`")
880
- absolute_feature_method_var_cnn = Connection.based_on(*relevant_abs_conn)
893
+ absolute_feature_method_var_cnn = Connection.based_on(OrConnectionRelation(*relevant_abs_conn))
881
894
  cur_method_variation = cur_setup_feature_controller.get_method_variation(
882
895
  of_method_name=cur_method_name, for_vdevice=mapped_vdevice,
883
896
  with_connection=absolute_feature_method_var_cnn)