baldertest 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- _balder/__init__.py +12 -0
- _balder/_version.py +34 -0
- _balder/balder_plugin.py +73 -0
- _balder/balder_session.py +341 -0
- _balder/balder_settings.py +15 -0
- _balder/cnnrelations/__init__.py +7 -0
- _balder/cnnrelations/and_connection_relation.py +176 -0
- _balder/cnnrelations/base_connection_relation.py +270 -0
- _balder/cnnrelations/or_connection_relation.py +65 -0
- _balder/collector.py +874 -0
- _balder/connection.py +863 -0
- _balder/connection_metadata.py +255 -0
- _balder/console/__init__.py +0 -0
- _balder/console/balder.py +58 -0
- _balder/controllers/__init__.py +12 -0
- _balder/controllers/base_device_controller.py +72 -0
- _balder/controllers/controller.py +29 -0
- _balder/controllers/device_controller.py +446 -0
- _balder/controllers/feature_controller.py +715 -0
- _balder/controllers/normal_scenario_setup_controller.py +402 -0
- _balder/controllers/scenario_controller.py +524 -0
- _balder/controllers/setup_controller.py +134 -0
- _balder/controllers/vdevice_controller.py +95 -0
- _balder/decorator_connect.py +104 -0
- _balder/decorator_covered_by.py +74 -0
- _balder/decorator_fixture.py +29 -0
- _balder/decorator_for_vdevice.py +118 -0
- _balder/decorator_gateway.py +34 -0
- _balder/decorator_insert_into_tree.py +52 -0
- _balder/decorator_parametrize.py +31 -0
- _balder/decorator_parametrize_by_feature.py +36 -0
- _balder/device.py +18 -0
- _balder/exceptions.py +182 -0
- _balder/executor/__init__.py +0 -0
- _balder/executor/basic_executable_executor.py +133 -0
- _balder/executor/basic_executor.py +205 -0
- _balder/executor/executor_tree.py +217 -0
- _balder/executor/parametrized_testcase_executor.py +52 -0
- _balder/executor/scenario_executor.py +169 -0
- _balder/executor/setup_executor.py +163 -0
- _balder/executor/testcase_executor.py +203 -0
- _balder/executor/unresolved_parametrized_testcase_executor.py +184 -0
- _balder/executor/variation_executor.py +882 -0
- _balder/exit_code.py +19 -0
- _balder/feature.py +74 -0
- _balder/feature_replacement_mapping.py +107 -0
- _balder/feature_vdevice_mapping.py +88 -0
- _balder/fixture_definition_scope.py +19 -0
- _balder/fixture_execution_level.py +22 -0
- _balder/fixture_manager.py +483 -0
- _balder/fixture_metadata.py +26 -0
- _balder/node_gateway.py +103 -0
- _balder/objects/__init__.py +0 -0
- _balder/objects/connections/__init__.py +0 -0
- _balder/objects/connections/osi_1_physical.py +116 -0
- _balder/objects/connections/osi_2_datalink.py +35 -0
- _balder/objects/connections/osi_3_network.py +47 -0
- _balder/objects/connections/osi_4_transport.py +40 -0
- _balder/objects/connections/osi_5_session.py +13 -0
- _balder/objects/connections/osi_6_presentation.py +13 -0
- _balder/objects/connections/osi_7_application.py +83 -0
- _balder/objects/devices/__init__.py +0 -0
- _balder/objects/devices/this_device.py +12 -0
- _balder/parametrization.py +75 -0
- _balder/plugin_manager.py +138 -0
- _balder/previous_executor_mark.py +23 -0
- _balder/routing_path.py +335 -0
- _balder/scenario.py +20 -0
- _balder/setup.py +18 -0
- _balder/solver.py +246 -0
- _balder/testresult.py +163 -0
- _balder/unmapped_vdevice.py +13 -0
- _balder/utils/__init__.py +0 -0
- _balder/utils/functions.py +103 -0
- _balder/utils/inner_device_managing_metaclass.py +14 -0
- _balder/utils/mixin_can_be_covered_by_executor.py +24 -0
- _balder/utils/typings.py +4 -0
- _balder/vdevice.py +9 -0
- balder/__init__.py +56 -0
- balder/connections.py +43 -0
- balder/devices.py +9 -0
- balder/exceptions.py +44 -0
- balder/parametrization.py +8 -0
- baldertest-0.1.0.dist-info/METADATA +356 -0
- baldertest-0.1.0.dist-info/RECORD +89 -0
- baldertest-0.1.0.dist-info/WHEEL +5 -0
- baldertest-0.1.0.dist-info/entry_points.txt +2 -0
- baldertest-0.1.0.dist-info/licenses/LICENSE +21 -0
- baldertest-0.1.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Type, List, Union, Optional, Dict, TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
import inspect
|
|
6
|
+
from abc import ABC, abstractmethod
|
|
7
|
+
from _balder.setup import Setup
|
|
8
|
+
from _balder.device import Device
|
|
9
|
+
from _balder.scenario import Scenario
|
|
10
|
+
from _balder.connection_metadata import ConnectionMetadata
|
|
11
|
+
from _balder.controllers.controller import Controller
|
|
12
|
+
from _balder.controllers.device_controller import DeviceController
|
|
13
|
+
from _balder.controllers.vdevice_controller import VDeviceController
|
|
14
|
+
from _balder.exceptions import MultiInheritanceError, DeviceOverwritingError, MissingFeaturesOfVDeviceError
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from _balder.connection import Connection
|
|
18
|
+
from _balder.controllers import ScenarioController
|
|
19
|
+
from _balder.controllers import SetupController
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__file__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class NormalScenarioSetupController(Controller, ABC):
|
|
25
|
+
"""
|
|
26
|
+
This is the abstract base controller class for the Scenario- and Setup-Controller.
|
|
27
|
+
"""
|
|
28
|
+
# describes if the current controller is for setups or for scenarios (has to be set in child controller)
|
|
29
|
+
_related_type: Optional[Type[Scenario, Setup]] = None
|
|
30
|
+
|
|
31
|
+
# ---------------------------------- STATIC METHODS ----------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
# ---------------------------------- CLASS METHODS -----------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
# ---------------------------------- PROPERTIES --------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
# ---------------------------------- PROTECTED METHODS -------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
# ---------------------------------- METHODS -----------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def get_for(related_cls) -> Union[ScenarioController, SetupController]:
|
|
43
|
+
"""
|
|
44
|
+
This class returns the current existing controller instance for the given item. If the instance does not exist
|
|
45
|
+
yet, it will automatically create it and saves the instance in an internal dictionary.
|
|
46
|
+
|
|
47
|
+
.. note::
|
|
48
|
+
This method automatically returns the correct controller type, depending on the class you provide with
|
|
49
|
+
`related_cls`.
|
|
50
|
+
"""
|
|
51
|
+
# pylint: disable-next=import-outside-toplevel
|
|
52
|
+
from _balder.controllers.setup_controller import SetupController
|
|
53
|
+
# pylint: disable-next=import-outside-toplevel
|
|
54
|
+
from _balder.controllers.scenario_controller import ScenarioController
|
|
55
|
+
|
|
56
|
+
if issubclass(related_cls, Scenario):
|
|
57
|
+
return ScenarioController.get_for(related_cls)
|
|
58
|
+
if issubclass(related_cls, Setup):
|
|
59
|
+
return SetupController.get_for(related_cls)
|
|
60
|
+
|
|
61
|
+
raise TypeError(f"illegal non supported type `{related_cls.__name__}` given for `related_cls`")
|
|
62
|
+
|
|
63
|
+
def get_all_inner_device_classes(self) -> List[Type[Device]]:
|
|
64
|
+
"""
|
|
65
|
+
This method provides a list of all :meth:`Device` classes that have been defined as inner classes in the related
|
|
66
|
+
scenario or setup.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
all_classes = inspect.getmembers(self.related_cls, inspect.isclass)
|
|
70
|
+
filtered_classes = []
|
|
71
|
+
for _, cur_class in all_classes:
|
|
72
|
+
if not issubclass(cur_class, Device):
|
|
73
|
+
# filter all classes and make sure that only the child classes of :meth:`Device` remain
|
|
74
|
+
continue
|
|
75
|
+
if DeviceController.get_for(cur_class).get_outer_class() != self.related_cls:
|
|
76
|
+
# filter all classes that do not match the setup name in __qualname__
|
|
77
|
+
continue
|
|
78
|
+
# otherwise, add this candidate
|
|
79
|
+
filtered_classes.append(cur_class)
|
|
80
|
+
return filtered_classes
|
|
81
|
+
|
|
82
|
+
def get_inner_device_class_by_string(self, device_str: str) -> Union[Type[Device], None]:
|
|
83
|
+
"""
|
|
84
|
+
This method returns the inner Device class for the given string.
|
|
85
|
+
|
|
86
|
+
:param device_str: the name string of the Device that should be returned
|
|
87
|
+
|
|
88
|
+
:return: the Device class or None, if the method has not found any class with this name
|
|
89
|
+
"""
|
|
90
|
+
possible_devs = [cur_vdevice for cur_vdevice in self.get_all_inner_device_classes()
|
|
91
|
+
if cur_vdevice.__name__ == device_str]
|
|
92
|
+
if len(possible_devs) == 0:
|
|
93
|
+
return None
|
|
94
|
+
if len(possible_devs) > 1:
|
|
95
|
+
raise RuntimeError("found more than one possible vDevices - something unexpected happened")
|
|
96
|
+
|
|
97
|
+
return possible_devs[0]
|
|
98
|
+
|
|
99
|
+
def get_all_abs_inner_device_classes(self) -> List[Type[Device]]:
|
|
100
|
+
"""
|
|
101
|
+
This method provides a list of all :meth:`Device` classes that are valid for the related scenario or setup
|
|
102
|
+
class. If the class itself does not implement some inner devices by its own, it returns the absolute inner
|
|
103
|
+
devices of the next higher setup/scenario parent class.
|
|
104
|
+
"""
|
|
105
|
+
cls_devices = self.get_all_inner_device_classes()
|
|
106
|
+
if len(cls_devices) == 0:
|
|
107
|
+
# search for parent class
|
|
108
|
+
base_class = self.get_next_parent_class()
|
|
109
|
+
|
|
110
|
+
if base_class is None:
|
|
111
|
+
# if the class type is the original `Setup` or `Scenario` type -> no inner devices exists
|
|
112
|
+
return []
|
|
113
|
+
return self.__class__.get_for(base_class).get_all_abs_inner_device_classes()
|
|
114
|
+
|
|
115
|
+
return cls_devices
|
|
116
|
+
|
|
117
|
+
def get_abs_inner_device_class_by_string(self, device_str: str) -> Union[Type[Device], None]:
|
|
118
|
+
"""
|
|
119
|
+
This method returns the absolute inner Device class for the given string.
|
|
120
|
+
|
|
121
|
+
:param device_str: the name string of the Device that should be returned
|
|
122
|
+
|
|
123
|
+
:return: the Device class or None, if the method has not found any class with this name
|
|
124
|
+
"""
|
|
125
|
+
possible_devs = [cur_vdevice for cur_vdevice in self.get_all_abs_inner_device_classes()
|
|
126
|
+
if cur_vdevice.__name__ == device_str]
|
|
127
|
+
if len(possible_devs) == 0:
|
|
128
|
+
return None
|
|
129
|
+
if len(possible_devs) > 1:
|
|
130
|
+
raise RuntimeError("found more than one possible vDevices - something unexpected happened")
|
|
131
|
+
|
|
132
|
+
return possible_devs[0]
|
|
133
|
+
|
|
134
|
+
@abstractmethod
|
|
135
|
+
def get_next_parent_class(self) -> Union[Type[Scenario], Type[Setup], None]:
|
|
136
|
+
"""
|
|
137
|
+
This method returns the next parent class which is a subclass of the :class:`Scenario`/:class:`Setup` itself.
|
|
138
|
+
|
|
139
|
+
:return: returns the next parent class or None if the next parent class is :class:`Scenario`/:class:`Setup`
|
|
140
|
+
itself
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
def get_all_connections(self) -> List[Connection]:
|
|
144
|
+
"""
|
|
145
|
+
This method returns all connection objects which have been defined between devices of the related class.
|
|
146
|
+
"""
|
|
147
|
+
all_device_classes = self.get_all_inner_device_classes()
|
|
148
|
+
all_connections = []
|
|
149
|
+
for cur_device in all_device_classes:
|
|
150
|
+
cur_device_controller = DeviceController.get_for(cur_device)
|
|
151
|
+
for cur_node_name in cur_device_controller.connections.keys():
|
|
152
|
+
all_connections += cur_device_controller.connections[cur_node_name]
|
|
153
|
+
return all_connections
|
|
154
|
+
|
|
155
|
+
def get_all_abs_connections(self) -> List[Connection]:
|
|
156
|
+
"""
|
|
157
|
+
This method returns all absolute connection objects which have been defined between absolute devices of the
|
|
158
|
+
related scenario or setup class.
|
|
159
|
+
"""
|
|
160
|
+
all_device_classes = self.get_all_abs_inner_device_classes()
|
|
161
|
+
all_connections = []
|
|
162
|
+
for cur_device in all_device_classes:
|
|
163
|
+
cur_device_controller = DeviceController.get_for(cur_device)
|
|
164
|
+
for _, cur_connections in cur_device_controller.absolute_connections.items():
|
|
165
|
+
for cur_cnn in cur_connections:
|
|
166
|
+
if cur_cnn not in all_connections:
|
|
167
|
+
all_connections.append(cur_cnn)
|
|
168
|
+
return all_connections
|
|
169
|
+
|
|
170
|
+
def validate_inheritance(self):
|
|
171
|
+
"""
|
|
172
|
+
This method validates that the inheritance of the related :class:`Setup`/:class:`Scenario` class was done
|
|
173
|
+
correctly. It checks that all inner devices that are inherited has the same naming as their parents and also
|
|
174
|
+
that every reused name (that is already be used for a device in the parent class) does also inherit from this
|
|
175
|
+
parent scenario/setup device.
|
|
176
|
+
|
|
177
|
+
In addition to that, it secures that either all devices are overwritten in the current class or no devices are
|
|
178
|
+
overwritten in the related class.
|
|
179
|
+
"""
|
|
180
|
+
|
|
181
|
+
# get parent scenario / setup class and check no multi inheritance
|
|
182
|
+
parent_scenario_or_setup = self.get_next_parent_class()
|
|
183
|
+
if parent_scenario_or_setup is None:
|
|
184
|
+
# done, because the parent class is direct Scenario/Setup class
|
|
185
|
+
return
|
|
186
|
+
|
|
187
|
+
parent_scenario_or_setup_controller = self.__class__.get_for(parent_scenario_or_setup)
|
|
188
|
+
|
|
189
|
+
devices = self.get_all_inner_device_classes()
|
|
190
|
+
abs_parent_devices = parent_scenario_or_setup_controller.get_all_abs_inner_device_classes()
|
|
191
|
+
abs_parent_devices_by_name = {cur_parent.__name__: cur_parent for cur_parent in abs_parent_devices}
|
|
192
|
+
|
|
193
|
+
if len(devices) == 0:
|
|
194
|
+
# ignore it because cur item has no own device definitions
|
|
195
|
+
return
|
|
196
|
+
|
|
197
|
+
# check that a device is newly defined or has the same name as the parent device
|
|
198
|
+
for cur_item_device in devices:
|
|
199
|
+
# check if name exists in parent
|
|
200
|
+
relevant_parent_according_naming = abs_parent_devices_by_name.get(cur_item_device.__name__, None)
|
|
201
|
+
|
|
202
|
+
# check if device is inherited from a parent
|
|
203
|
+
relevant_parent_device_according_inheritance = None
|
|
204
|
+
for cur_parent in abs_parent_devices:
|
|
205
|
+
if issubclass(cur_item_device, cur_parent):
|
|
206
|
+
if relevant_parent_device_according_inheritance is not None:
|
|
207
|
+
# multi inheritance is not allowed
|
|
208
|
+
raise MultiInheritanceError(
|
|
209
|
+
f"found more than one {self._related_type.__name__}-Device parent classes for the "
|
|
210
|
+
f"class `{cur_item_device.__name__}` - multi inheritance is not allowed for device "
|
|
211
|
+
f"classes")
|
|
212
|
+
relevant_parent_device_according_inheritance = cur_parent
|
|
213
|
+
|
|
214
|
+
# now check if both is fulfilled
|
|
215
|
+
if relevant_parent_according_naming == relevant_parent_device_according_inheritance and \
|
|
216
|
+
relevant_parent_device_according_inheritance is not None:
|
|
217
|
+
# device is inherited AND has the same name as used in parent -> ALLOWED
|
|
218
|
+
pass
|
|
219
|
+
elif relevant_parent_according_naming is None and relevant_parent_device_according_inheritance is None:
|
|
220
|
+
# both are none -> it is a new device -> ALLOWED
|
|
221
|
+
pass
|
|
222
|
+
elif relevant_parent_according_naming is None:
|
|
223
|
+
# reused a naming but does not inherit from it -> NOT ALLOWED
|
|
224
|
+
raise DeviceOverwritingError(
|
|
225
|
+
f"the inner device class `{cur_item_device.__qualname__}` which inherits from another "
|
|
226
|
+
f"device `{relevant_parent_device_according_inheritance.__qualname__}` - it should also have "
|
|
227
|
+
f"the same name")
|
|
228
|
+
elif relevant_parent_device_according_inheritance is None:
|
|
229
|
+
# inherit from a parent device, but it doesn't have the same naming -> NOT ALLOWED
|
|
230
|
+
raise DeviceOverwritingError(
|
|
231
|
+
f"the inner device class `{cur_item_device.__qualname__}` has the same name than the "
|
|
232
|
+
f"device `{relevant_parent_according_naming.__qualname__}` - it should also inherit from it")
|
|
233
|
+
|
|
234
|
+
# secure that all parent devices are implemented here too
|
|
235
|
+
for cur_parent in abs_parent_devices:
|
|
236
|
+
found_parent = len([dev for dev in devices if issubclass(dev, cur_parent)]) > 0
|
|
237
|
+
if not found_parent:
|
|
238
|
+
raise DeviceOverwritingError(
|
|
239
|
+
f"found a device `{cur_parent.__qualname__}` which is part of a parent class, but it is "
|
|
240
|
+
f"not implemented in child class `{self.related_cls.__name__}`")
|
|
241
|
+
|
|
242
|
+
# also check the parent class here
|
|
243
|
+
self.__class__.get_for(parent_scenario_or_setup).validate_inheritance()
|
|
244
|
+
|
|
245
|
+
def check_vdevice_feature_existence(self):
|
|
246
|
+
"""
|
|
247
|
+
This method validates that the :class:`Feature` property set of a :class:`Device` holds all required
|
|
248
|
+
:class:`Feature` objects of the related :class:`VDevice`. For this the method checks that every feature (that
|
|
249
|
+
is used in a mapped :class:`VDevice`) also exists as a child :class:`Feature` property in the related
|
|
250
|
+
:class:`Device` class.
|
|
251
|
+
|
|
252
|
+
.. note::
|
|
253
|
+
Variations are not related to this and will not be checked here.
|
|
254
|
+
|
|
255
|
+
"""
|
|
256
|
+
|
|
257
|
+
for cur_device in self.get_all_abs_inner_device_classes():
|
|
258
|
+
cur_device_instantiated_features = \
|
|
259
|
+
DeviceController.get_for(cur_device).get_all_instantiated_feature_objects()
|
|
260
|
+
for _, cur_feature in cur_device_instantiated_features.items():
|
|
261
|
+
active_vdevice, related_device = cur_feature.active_vdevice_device_mapping
|
|
262
|
+
if active_vdevice is None:
|
|
263
|
+
# ignore this, because no active vdevice exists
|
|
264
|
+
continue
|
|
265
|
+
|
|
266
|
+
# secure that all the defined features in the VDevice also exist in the related device ->
|
|
267
|
+
# otherwise error
|
|
268
|
+
orig_device_features = [
|
|
269
|
+
feat for _, feat in
|
|
270
|
+
DeviceController.get_for(related_device).get_all_instantiated_feature_objects().items()]
|
|
271
|
+
active_vdevice_instantiated_features = \
|
|
272
|
+
VDeviceController.get_for(active_vdevice).get_all_instantiated_feature_objects()
|
|
273
|
+
for _, cur_vdevice_feature in active_vdevice_instantiated_features.items():
|
|
274
|
+
# search for it
|
|
275
|
+
found_it = False
|
|
276
|
+
for cur_orig_feature in orig_device_features:
|
|
277
|
+
if isinstance(cur_orig_feature, cur_vdevice_feature.__class__):
|
|
278
|
+
found_it = True
|
|
279
|
+
break
|
|
280
|
+
if not found_it:
|
|
281
|
+
raise MissingFeaturesOfVDeviceError(
|
|
282
|
+
f"the device `{related_device.__name__}` which is mapped to the VDevice "
|
|
283
|
+
f"`{active_vdevice.__name__}` doesn't have an implementation for the feature "
|
|
284
|
+
f"`{cur_vdevice_feature.__class__.__name__}` required by the VDevice class "
|
|
285
|
+
f"`{active_vdevice.__name__}`")
|
|
286
|
+
|
|
287
|
+
def get_absolute_single_connections(self) -> \
|
|
288
|
+
Dict[Type[Device], Dict[str, Dict[Type[Device], Dict[str, List[Connection]]]]]:
|
|
289
|
+
"""
|
|
290
|
+
This method determines the synchronized (both devices of a connection were updated) absolute connections
|
|
291
|
+
between all devices of this scenario/setup.
|
|
292
|
+
|
|
293
|
+
:return: returns a dictionary which provides all single connections between two devices that can be accessed
|
|
294
|
+
with `result[dev1][node-of-dev1][dev2][node-of-dev2]`
|
|
295
|
+
"""
|
|
296
|
+
# start to generate the singles for every connection between the devices of every scenario
|
|
297
|
+
all_abs_single_connections = {}
|
|
298
|
+
|
|
299
|
+
all_devices = self.get_all_abs_inner_device_classes()
|
|
300
|
+
for cur_from_device in all_devices:
|
|
301
|
+
cur_from_device_controller = DeviceController.get_for(cur_from_device)
|
|
302
|
+
|
|
303
|
+
# generate the whole `all_abs_single_connections` and convert the connections to singles
|
|
304
|
+
for cur_to_device, cur_connections in cur_from_device_controller.absolute_connections.items():
|
|
305
|
+
for cur_cnn in cur_connections:
|
|
306
|
+
if cur_from_device == cur_cnn.from_device:
|
|
307
|
+
cur_from_node = cur_cnn.from_node_name
|
|
308
|
+
cur_to_node = cur_cnn.to_node_name
|
|
309
|
+
else:
|
|
310
|
+
cur_from_node = cur_cnn.to_node_name
|
|
311
|
+
cur_to_node = cur_cnn.from_node_name
|
|
312
|
+
if cur_from_device not in all_abs_single_connections.keys():
|
|
313
|
+
all_abs_single_connections[cur_from_device] = {}
|
|
314
|
+
if cur_from_node not in all_abs_single_connections[cur_from_device].keys():
|
|
315
|
+
all_abs_single_connections[cur_from_device][cur_from_node] = {}
|
|
316
|
+
if cur_to_device not in \
|
|
317
|
+
all_abs_single_connections[cur_from_device][cur_from_node].keys():
|
|
318
|
+
all_abs_single_connections[cur_from_device][
|
|
319
|
+
cur_from_node][cur_to_device] = {}
|
|
320
|
+
if cur_to_node not in \
|
|
321
|
+
all_abs_single_connections[cur_from_device][cur_from_node][cur_to_device].keys():
|
|
322
|
+
all_abs_single_connections[cur_from_device][cur_from_node][cur_to_device][
|
|
323
|
+
cur_to_node] = cur_cnn.get_singles()
|
|
324
|
+
# we do not have to set the connection in communication device, because the absolute
|
|
325
|
+
# connections are always synchronized
|
|
326
|
+
else:
|
|
327
|
+
raise ValueError(
|
|
328
|
+
f'found multiple definitions for connection from device '
|
|
329
|
+
f'{cur_from_device.__qualname__} (node: `{cur_from_node}`) to device '
|
|
330
|
+
f'{cur_to_device.__qualname__} (node: `{cur_to_node}`) in scenario '
|
|
331
|
+
f'`{self.related_cls.__name__}`')
|
|
332
|
+
return all_abs_single_connections
|
|
333
|
+
|
|
334
|
+
def determine_raw_absolute_device_connections(self):
|
|
335
|
+
"""
|
|
336
|
+
This method determines and creates the basic `_absolute_connections` for the related scenario/setup. Note,
|
|
337
|
+
that this method only creates the class attribute and adds the synchronized connections (same on both sides if
|
|
338
|
+
they are bidirectional). It does not analyse or take :class:`Feature` classes into consideration.
|
|
339
|
+
"""
|
|
340
|
+
# determine next relevant base class
|
|
341
|
+
next_base_class = self.get_next_parent_class()
|
|
342
|
+
|
|
343
|
+
# executed this method for all parents too
|
|
344
|
+
if next_base_class:
|
|
345
|
+
NormalScenarioSetupController.get_for(next_base_class).determine_raw_absolute_device_connections()
|
|
346
|
+
|
|
347
|
+
all_relevant_cnns = []
|
|
348
|
+
|
|
349
|
+
all_devices = self.get_all_inner_device_classes()
|
|
350
|
+
all_devices_as_strings = [search_device.__name__ for search_device in all_devices]
|
|
351
|
+
|
|
352
|
+
# check if the devices of the current item has minimum one own connect() decorator
|
|
353
|
+
has_connect_decorator = False
|
|
354
|
+
for cur_device in all_devices:
|
|
355
|
+
if len(DeviceController.get_for(cur_device).connections) > 0:
|
|
356
|
+
has_connect_decorator = True
|
|
357
|
+
|
|
358
|
+
if len(all_devices) == 0 and len(self.get_all_abs_inner_device_classes()) > 0:
|
|
359
|
+
# only the parent class has defined scenarios -> use absolute data from next parent
|
|
360
|
+
# NOTHING TO DO, because we also use these devices in child setup/scenario
|
|
361
|
+
return
|
|
362
|
+
|
|
363
|
+
if len(all_devices) > 0 and not has_connect_decorator:
|
|
364
|
+
# the current item has defined devices, but no own `@connect()` decorator -> use absolute data from
|
|
365
|
+
# next parent
|
|
366
|
+
if next_base_class is not None:
|
|
367
|
+
# only if there is a next base class
|
|
368
|
+
next_base_class_controller = NormalScenarioSetupController.get_for(next_base_class)
|
|
369
|
+
|
|
370
|
+
for cur_parent_cnn in next_base_class_controller.get_all_abs_connections():
|
|
371
|
+
|
|
372
|
+
# find all related devices (for this connection)
|
|
373
|
+
related_from_device = \
|
|
374
|
+
all_devices[all_devices_as_strings.index(cur_parent_cnn.from_device.__name__)]
|
|
375
|
+
related_to_device = all_devices[all_devices_as_strings.index(cur_parent_cnn.to_device.__name__)]
|
|
376
|
+
new_cnn = cur_parent_cnn.clone()
|
|
377
|
+
new_cnn.set_metadata_for_all_subitems(ConnectionMetadata(
|
|
378
|
+
from_device=related_from_device, from_device_node_name=cur_parent_cnn.from_node_name,
|
|
379
|
+
to_device=related_to_device, to_device_node_name=cur_parent_cnn.to_node_name)
|
|
380
|
+
)
|
|
381
|
+
all_relevant_cnns.append(new_cnn)
|
|
382
|
+
|
|
383
|
+
# throw warning (but only if this scenario/setup has minimum one of the parent classes has inner
|
|
384
|
+
# devices (and connection between them) by its own)
|
|
385
|
+
if len(next_base_class_controller.get_all_abs_inner_device_classes()) > 0 and \
|
|
386
|
+
len(next_base_class_controller.get_all_abs_connections()) > 0:
|
|
387
|
+
logger.warning(
|
|
388
|
+
f"the collected `{self.related_cls.__name__}` class overwrites devices, but does "
|
|
389
|
+
f"not define connections between them by its own - please provide them in case you "
|
|
390
|
+
f"overwrite devices")
|
|
391
|
+
else:
|
|
392
|
+
# otherwise, use data from current layer, because there is no parent, no devices or this item overwrites
|
|
393
|
+
# the connections from higher classes
|
|
394
|
+
for cur_device in all_devices:
|
|
395
|
+
for _, cur_cnn_list in DeviceController.get_for(cur_device).connections.items():
|
|
396
|
+
# now add every single connection correctly into the dictionary
|
|
397
|
+
all_relevant_cnns += [cur_cnn for cur_cnn in cur_cnn_list if cur_cnn not in all_relevant_cnns]
|
|
398
|
+
|
|
399
|
+
# now set the absolute connections correctly
|
|
400
|
+
for cur_cnn in all_relevant_cnns:
|
|
401
|
+
DeviceController.get_for(cur_cnn.from_device).add_new_absolute_connection(cur_cnn)
|
|
402
|
+
DeviceController.get_for(cur_cnn.to_device).add_new_absolute_connection(cur_cnn)
|