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,255 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Union, Type, Tuple
|
|
3
|
+
from .device import Device
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ConnectionMetadata:
|
|
7
|
+
"""
|
|
8
|
+
Describes the metadata of a connection.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
from_device: Union[Type[Device], None] = None,
|
|
14
|
+
to_device: Union[Type[Device], None] = None,
|
|
15
|
+
from_device_node_name: Union[str, None] = None,
|
|
16
|
+
to_device_node_name: Union[str, None] = None,
|
|
17
|
+
bidirectional: bool = True,
|
|
18
|
+
):
|
|
19
|
+
|
|
20
|
+
self._from_device = None
|
|
21
|
+
self._from_device_node_name = None
|
|
22
|
+
self.set_from(from_device, from_device_node_name)
|
|
23
|
+
|
|
24
|
+
self._to_device = None
|
|
25
|
+
self._to_device_node_name = None
|
|
26
|
+
self.set_to(to_device, to_device_node_name)
|
|
27
|
+
|
|
28
|
+
if not ((from_device is None and to_device is None and from_device_node_name is None
|
|
29
|
+
and to_device_node_name is None) or (
|
|
30
|
+
from_device is not None and to_device is not None and from_device_node_name is not None and
|
|
31
|
+
to_device_node_name is not None)):
|
|
32
|
+
raise ValueError(
|
|
33
|
+
"you have to provide all or none of the following items: `from_device`, `from_device_node_name`, "
|
|
34
|
+
"`to_device` or `to_device_node_name`")
|
|
35
|
+
|
|
36
|
+
# describes if the connection is uni or bidirectional
|
|
37
|
+
self._bidirectional = bidirectional
|
|
38
|
+
|
|
39
|
+
def __eq__(self, other: ConnectionMetadata):
|
|
40
|
+
return self.equal_with(other)
|
|
41
|
+
|
|
42
|
+
def __hash__(self):
|
|
43
|
+
all_hashes = hash(self._from_device) + hash(self._to_device) + hash(self._from_device_node_name) + \
|
|
44
|
+
hash(self._to_device_node_name) + hash(self._bidirectional)
|
|
45
|
+
return hash(all_hashes)
|
|
46
|
+
|
|
47
|
+
def __compare_with(self, other: ConnectionMetadata, allow_single_unidirectional_for_both_directions: bool) -> bool:
|
|
48
|
+
"""
|
|
49
|
+
This method checks, if the metadata of this object is the metadata of the other object.
|
|
50
|
+
|
|
51
|
+
The method returns true in the following situations:
|
|
52
|
+
* both connections are bidirectional / the FROM and TO elements (device and node name) are the same
|
|
53
|
+
* both connections are bidirectional / the FROM is the TO and the TO is the FROM
|
|
54
|
+
* both connections are unidirectional and have the same from and to elements
|
|
55
|
+
|
|
56
|
+
If the parameter `allow_single_unidirectional_for_both_directions` is True, it additionally checks the following
|
|
57
|
+
situations:
|
|
58
|
+
* one is unidirectional / the other is bidirectional / the FROM and TO elements are the same
|
|
59
|
+
* one is unidirectional / the other is bidirectional / the FROM is the TO and the TO is the FROM
|
|
60
|
+
"""
|
|
61
|
+
def check_same() -> bool:
|
|
62
|
+
return (self.from_device == other.from_device and self.from_node_name == other.from_node_name and
|
|
63
|
+
self.to_device == other.to_device and self.to_node_name == other.to_node_name)
|
|
64
|
+
|
|
65
|
+
def check_twisted() -> bool:
|
|
66
|
+
return (self.from_device == other.to_device and self.from_node_name == other.to_node_name and
|
|
67
|
+
self.to_device == other.from_device and self.to_node_name == other.from_node_name)
|
|
68
|
+
|
|
69
|
+
# CHECK: both connections are bidirectional / the FROM and TO elements (device and node name) are the same
|
|
70
|
+
# CHECK: both connections are bidirectional / the FROM is the TO and the TO is the FROM
|
|
71
|
+
if self.bidirectional and other.bidirectional:
|
|
72
|
+
return check_same() or check_twisted()
|
|
73
|
+
# CHECK: both connections are unidirectional and have the same from and to elements
|
|
74
|
+
if not self.bidirectional and not other.bidirectional:
|
|
75
|
+
return check_same()
|
|
76
|
+
|
|
77
|
+
if allow_single_unidirectional_for_both_directions:
|
|
78
|
+
# CHECK: one is unidirectional / the other is bidirectional / the FROM and TO elements are the same
|
|
79
|
+
# CHECK: one is unidirectional / the other is bidirectional / the FROM is the TO and the TO is the FROM
|
|
80
|
+
if self.bidirectional and not other.bidirectional or not self.bidirectional and other.bidirectional:
|
|
81
|
+
return check_same() or check_twisted()
|
|
82
|
+
return False
|
|
83
|
+
|
|
84
|
+
def set_from(self, from_device: Union[Type[Device], None], from_device_node_name: Union[str, None] = None):
|
|
85
|
+
"""
|
|
86
|
+
This method sets the FROM device and node for this connection.
|
|
87
|
+
|
|
88
|
+
:param from_device: The FROM device of this connection.
|
|
89
|
+
:param from_device_node_name: The FROM node of this connection (if it should be set, otherwise None).
|
|
90
|
+
"""
|
|
91
|
+
if from_device is not None and isinstance(from_device, type) and not issubclass(from_device, Device):
|
|
92
|
+
raise TypeError(f"detect illegal argument element {str(from_device)} for given attribute "
|
|
93
|
+
f"`from_device` - should be a subclasses of `balder.Device`")
|
|
94
|
+
self._from_device = from_device
|
|
95
|
+
|
|
96
|
+
if from_device_node_name is not None and not isinstance(from_device_node_name, str):
|
|
97
|
+
raise TypeError(f"detect illegal argument type {type(from_device_node_name)} for given attribute "
|
|
98
|
+
f"`from_device_node_name` - should be a string value")
|
|
99
|
+
self._from_device_node_name = from_device_node_name
|
|
100
|
+
|
|
101
|
+
def set_to(self, to_device: Union[Type[Device], None], to_device_node_name: Union[str, None] = None):
|
|
102
|
+
"""
|
|
103
|
+
This method sets the TO device and node of this connection.
|
|
104
|
+
|
|
105
|
+
:param to_device: The TO device of this connection.
|
|
106
|
+
:param to_device_node_name: The TO node of this connection (if it should be set, otherwise None).
|
|
107
|
+
"""
|
|
108
|
+
if to_device is not None and isinstance(to_device, type) and not issubclass(to_device, Device):
|
|
109
|
+
raise TypeError(f"detect illegal argument element {str(to_device)} for given attribute "
|
|
110
|
+
f"`to_device` - should be a subclasses of `balder.Device`")
|
|
111
|
+
self._to_device = to_device
|
|
112
|
+
|
|
113
|
+
if to_device_node_name is not None and not isinstance(to_device_node_name, str):
|
|
114
|
+
raise TypeError(f"detect illegal argument type {type(to_device_node_name)} for given attribute "
|
|
115
|
+
f"`to_device_node_name` - should be a string value")
|
|
116
|
+
self._to_device_node_name = to_device_node_name
|
|
117
|
+
|
|
118
|
+
def get_conn_partner_of(self, device: Type[Device], node: Union[str, None] = None) -> Tuple[Type[Device], str]:
|
|
119
|
+
"""
|
|
120
|
+
This method returns the connection partner of this connection - it always returns the other not given side
|
|
121
|
+
|
|
122
|
+
:param device: the device itself - the other will be returned
|
|
123
|
+
|
|
124
|
+
:param node: the node name of the device itself (only required if the connection starts and ends with the same
|
|
125
|
+
device)
|
|
126
|
+
"""
|
|
127
|
+
if device not in (self.from_device, self.to_device):
|
|
128
|
+
raise ValueError(f"the given device `{device.__qualname__}` is no component of this connection")
|
|
129
|
+
if node is None:
|
|
130
|
+
# check that the from_device and to_device are not the same
|
|
131
|
+
if self.from_device == self.to_device:
|
|
132
|
+
raise ValueError("the connection is a inner-device connection (start and end is the same device) - you "
|
|
133
|
+
"have to provide the `node` string too")
|
|
134
|
+
if device == self.from_device:
|
|
135
|
+
return self.to_device, self.to_node_name
|
|
136
|
+
|
|
137
|
+
return self.from_device, self.from_node_name
|
|
138
|
+
|
|
139
|
+
if node not in (self.from_node_name, self.to_node_name):
|
|
140
|
+
raise ValueError(f"the given node `{node}` is no component of this connection")
|
|
141
|
+
|
|
142
|
+
if device == self.from_device and node == self.from_node_name:
|
|
143
|
+
return self.to_device, self.to_node_name
|
|
144
|
+
|
|
145
|
+
if device == self.to_device and node == self.to_node_name:
|
|
146
|
+
return self.from_device, self.from_node_name
|
|
147
|
+
|
|
148
|
+
raise ValueError(f"the given node `{node}` is no component of the given device `{device.__qualname__}`")
|
|
149
|
+
|
|
150
|
+
def has_connection_from_to(
|
|
151
|
+
self,
|
|
152
|
+
start_device: Type[Device],
|
|
153
|
+
start_device_node_name: Union[str, None] = None,
|
|
154
|
+
end_device: Union[Type[Device], None] = None,
|
|
155
|
+
end_device_node_name: Union[str, None] = None,
|
|
156
|
+
) -> bool:
|
|
157
|
+
"""
|
|
158
|
+
This method checks if there is a connection from ``start_device`` to ``end_device``. This will return
|
|
159
|
+
true if the ``start_device`` and ``end_device`` given in this method are also the ``start_device`` and
|
|
160
|
+
``end_device`` mentioned in this connection object. If this is a bidirectional connection, ``start_device`` and
|
|
161
|
+
``end_device`` can switch places.
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
:param start_device: the device for which the method should check whether it is a communication partner (for
|
|
165
|
+
non-bidirectional connection, this has to be the start device)
|
|
166
|
+
:param start_device_node_name: the node name that start device should have or None if it should be ignored
|
|
167
|
+
:param end_device: the other device for which the method should check whether it is a communication partner (for
|
|
168
|
+
non-bidirectional connection, this has to be the end device - this is optional if only the
|
|
169
|
+
start device should be checked)
|
|
170
|
+
:param end_device_node_name: the node name that start device should have or None if it should be ignored
|
|
171
|
+
|
|
172
|
+
:return: returns true if the given direction is possible
|
|
173
|
+
"""
|
|
174
|
+
if not (isinstance(start_device, type) and issubclass(start_device, Device)):
|
|
175
|
+
raise TypeError("argument `start_device` needs to be a device")
|
|
176
|
+
if not (start_device_node_name is None or isinstance(start_device_node_name, str)):
|
|
177
|
+
raise TypeError("argument `start_device_node_name` needs to be a string or None if it should be ignored")
|
|
178
|
+
if not (end_device is None or isinstance(end_device, type) and issubclass(end_device, Device)):
|
|
179
|
+
raise TypeError("argument `end_device` needs to be a device or None if it should be ignored")
|
|
180
|
+
if not (end_device_node_name is None or isinstance(end_device_node_name, str)):
|
|
181
|
+
raise TypeError("argument `end_device_node_name` needs to be a string or None if it should be ignored")
|
|
182
|
+
|
|
183
|
+
def check(start_dev, start_node_name, end_dev, end_node_name):
|
|
184
|
+
if start_device != start_dev:
|
|
185
|
+
return False
|
|
186
|
+
if start_device_node_name is not None and start_device_node_name != start_node_name:
|
|
187
|
+
return False
|
|
188
|
+
if end_device is not None and end_device != end_dev:
|
|
189
|
+
return False
|
|
190
|
+
if end_device_node_name is not None and end_device_node_name != end_node_name:
|
|
191
|
+
return False
|
|
192
|
+
return True
|
|
193
|
+
|
|
194
|
+
if self.bidirectional:
|
|
195
|
+
if check(start_dev=self.to_device, start_node_name=self.to_node_name,
|
|
196
|
+
end_dev=self.from_device, end_node_name=self.from_node_name):
|
|
197
|
+
return True
|
|
198
|
+
return check(start_dev=self.from_device, start_node_name=self.from_node_name,
|
|
199
|
+
end_dev=self.to_device, end_node_name=self.to_node_name)
|
|
200
|
+
|
|
201
|
+
def equal_with(self, other: ConnectionMetadata) -> bool:
|
|
202
|
+
"""
|
|
203
|
+
This method returns true if the metadata of the current connection is equal with the metadata of the given
|
|
204
|
+
connection.
|
|
205
|
+
|
|
206
|
+
The method returns true in the following situations:
|
|
207
|
+
* both connections are bidirectional and the from and to elements (device and node name) are the same
|
|
208
|
+
* both connections are unidirectional and have the same from and to elements
|
|
209
|
+
* both connections are bidirectional and the from is the to and the to is the from
|
|
210
|
+
|
|
211
|
+
:return: true if the metadata of the current connection is contained in the metadata of the given one
|
|
212
|
+
"""
|
|
213
|
+
return self.__compare_with(other, allow_single_unidirectional_for_both_directions=False)
|
|
214
|
+
|
|
215
|
+
def contained_in(self, other: ConnectionMetadata) -> bool:
|
|
216
|
+
"""
|
|
217
|
+
This method returns true if the metadata of the current connection is contained in the given one.
|
|
218
|
+
|
|
219
|
+
The method returns true in the following situations:
|
|
220
|
+
* both connections are bidirectional and the from and to elements (device and node name) are the same
|
|
221
|
+
* both connections are unidirectional and have the same from and to elements
|
|
222
|
+
* both connections are bidirectional and the from is the to and the to is the from
|
|
223
|
+
* one connection is unidirectional and the other is bidirectional and the from and to elements are the same
|
|
224
|
+
* one connection is unidirectional and the other is bidirectional and the from is the to and the to is the from
|
|
225
|
+
|
|
226
|
+
:return: true if the metadata of the current connection is contained in the metadata of the given one
|
|
227
|
+
"""
|
|
228
|
+
return self.__compare_with(other, allow_single_unidirectional_for_both_directions=True)
|
|
229
|
+
|
|
230
|
+
@property
|
|
231
|
+
def from_device(self):
|
|
232
|
+
"""device from which the connection starts"""
|
|
233
|
+
return self._from_device
|
|
234
|
+
|
|
235
|
+
@property
|
|
236
|
+
def to_device(self):
|
|
237
|
+
"""device at which the connection ends"""
|
|
238
|
+
return self._to_device
|
|
239
|
+
|
|
240
|
+
@property
|
|
241
|
+
def from_node_name(self):
|
|
242
|
+
"""the name of the node in the `Device` from which the connection starts"""
|
|
243
|
+
return self._from_device_node_name
|
|
244
|
+
|
|
245
|
+
@property
|
|
246
|
+
def to_node_name(self):
|
|
247
|
+
"""the name of the node in the `Device` at which the connection ends"""
|
|
248
|
+
return self._to_device_node_name
|
|
249
|
+
|
|
250
|
+
@property
|
|
251
|
+
def bidirectional(self) -> bool:
|
|
252
|
+
"""
|
|
253
|
+
returns true if the connection is bidirectional (can go in both directions) otherwise false
|
|
254
|
+
"""
|
|
255
|
+
return self._bidirectional
|
|
File without changes
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import pathlib
|
|
4
|
+
import sys
|
|
5
|
+
import traceback
|
|
6
|
+
from typing import Callable, Optional, Union, List
|
|
7
|
+
from _balder.exit_code import ExitCode
|
|
8
|
+
from _balder.testresult import ResultState
|
|
9
|
+
from _balder.exceptions import BalderException
|
|
10
|
+
from _balder.balder_session import BalderSession
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def console_balder(cmd_args: Optional[List[str]] = None, working_dir: Union[str, pathlib.Path, None] = None):
|
|
14
|
+
"""script that executes a balder session"""
|
|
15
|
+
_console_balder_debug(cmd_args=cmd_args, working_dir=working_dir)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# pylint: disable-next=too-many-arguments
|
|
19
|
+
def _console_balder_debug(cmd_args: Optional[List[str]] = None, working_dir: Union[str, pathlib.Path, None] = None,
|
|
20
|
+
cb_session_created: Optional[Callable] = None, cb_run_finished: Optional[Callable] = None,
|
|
21
|
+
cb_balder_exc: Optional[Callable] = None, cb_unexpected_exc: Optional[Callable] = None):
|
|
22
|
+
"""helper balder execution that allows more debug access"""
|
|
23
|
+
try:
|
|
24
|
+
balder_session = BalderSession(cmd_args=cmd_args, working_dir=working_dir)
|
|
25
|
+
|
|
26
|
+
if cb_session_created:
|
|
27
|
+
cb_session_created(balder_session)
|
|
28
|
+
|
|
29
|
+
balder_session.run()
|
|
30
|
+
|
|
31
|
+
if cb_run_finished:
|
|
32
|
+
cb_run_finished(balder_session)
|
|
33
|
+
|
|
34
|
+
if balder_session.executor_tree is None:
|
|
35
|
+
sys.exit(ExitCode.SUCCESS.value)
|
|
36
|
+
elif balder_session.executor_tree.executor_result == ResultState.SUCCESS:
|
|
37
|
+
sys.exit(ExitCode.SUCCESS.value)
|
|
38
|
+
elif balder_session.executor_tree.executor_result in [ResultState.ERROR, ResultState.FAILURE]:
|
|
39
|
+
# check if a BalderException was thrown too -> would be a balder environment error -> special exit code
|
|
40
|
+
balder_exceptions = [cur_exc for cur_exc in balder_session.executor_tree.get_all_recognized_exception()
|
|
41
|
+
if isinstance(cur_exc, BalderException)]
|
|
42
|
+
if len(balder_exceptions) > 0:
|
|
43
|
+
sys.exit(ExitCode.BALDER_USAGE_ERROR.value)
|
|
44
|
+
else:
|
|
45
|
+
sys.exit(ExitCode.TESTS_FAILED.value)
|
|
46
|
+
|
|
47
|
+
except BalderException as exc:
|
|
48
|
+
# a balder usage error occurs
|
|
49
|
+
if cb_balder_exc:
|
|
50
|
+
cb_balder_exc(exc)
|
|
51
|
+
traceback.print_exception(*sys.exc_info())
|
|
52
|
+
sys.exit(ExitCode.BALDER_USAGE_ERROR.value)
|
|
53
|
+
except Exception as exc: # pylint: disable=broad-exception-caught
|
|
54
|
+
# a unexpected error occurs
|
|
55
|
+
if cb_unexpected_exc:
|
|
56
|
+
cb_unexpected_exc(exc)
|
|
57
|
+
traceback.print_exception(*sys.exc_info())
|
|
58
|
+
sys.exit(ExitCode.UNEXPECTED_ERROR.value)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from _balder.controllers.controller import Controller
|
|
2
|
+
|
|
3
|
+
from _balder.controllers.base_device_controller import BaseDeviceController
|
|
4
|
+
from _balder.controllers.device_controller import DeviceController
|
|
5
|
+
|
|
6
|
+
from _balder.controllers.vdevice_controller import VDeviceController
|
|
7
|
+
|
|
8
|
+
from _balder.controllers.normal_scenario_setup_controller import NormalScenarioSetupController
|
|
9
|
+
from _balder.controllers.scenario_controller import ScenarioController
|
|
10
|
+
from _balder.controllers.setup_controller import SetupController
|
|
11
|
+
|
|
12
|
+
from _balder.controllers.feature_controller import FeatureController
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Dict, Type, Union, TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
import inspect
|
|
6
|
+
from abc import ABC, abstractmethod
|
|
7
|
+
from _balder.controllers.controller import Controller
|
|
8
|
+
from _balder.feature import Feature
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from _balder.scenario import Scenario
|
|
12
|
+
from _balder.setup import Setup
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__file__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BaseDeviceController(Controller, ABC):
|
|
18
|
+
"""
|
|
19
|
+
This is the abstract controller class for :class:`Device` items.
|
|
20
|
+
"""
|
|
21
|
+
def __init__(self):
|
|
22
|
+
|
|
23
|
+
#: contains the original instantiated objects for the related device class (will be automatically set by
|
|
24
|
+
#: :class:`Collector`)
|
|
25
|
+
self._original_instanced_features: Union[Dict[str, Feature], None] = {}
|
|
26
|
+
|
|
27
|
+
# ---------------------------------- STATIC METHODS ----------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
# ---------------------------------- CLASS METHODS -----------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
# ---------------------------------- PROPERTIES --------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
# ---------------------------------- PROTECTED METHODS -------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
# ---------------------------------- METHODS -----------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def get_outer_class(self) -> Union[Type[Scenario], Type[Setup], None]:
|
|
39
|
+
"""
|
|
40
|
+
This method delivers the outer class of this device. This has to be a :meth:`Setup` or a :meth:`Scenario`.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def get_all_instantiated_feature_objects(self) -> Dict[str, Feature]:
|
|
44
|
+
"""
|
|
45
|
+
This method returns all instantiated :meth:`Feature` classes that were defined as static attributes within the
|
|
46
|
+
related device.
|
|
47
|
+
|
|
48
|
+
:return: supplies a dictionary with the name of the attribute as key and the current feature class as value
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
results = {}
|
|
52
|
+
for cur_attr_name, cur_elem in inspect.getmembers(self.related_cls, lambda elem: isinstance(elem, Feature)):
|
|
53
|
+
results[cur_attr_name] = cur_elem
|
|
54
|
+
return results
|
|
55
|
+
|
|
56
|
+
def get_original_instanced_feature_objects(self) -> Dict[str, Feature]:
|
|
57
|
+
"""
|
|
58
|
+
This method returns the original instanced feature objects of the related device
|
|
59
|
+
"""
|
|
60
|
+
if self._original_instanced_features is None:
|
|
61
|
+
raise RuntimeError('can not access the original instantiated features before they were set with '
|
|
62
|
+
'`save_all_original_instanced_features`')
|
|
63
|
+
return self._original_instanced_features
|
|
64
|
+
|
|
65
|
+
def save_all_original_instanced_features(self):
|
|
66
|
+
"""
|
|
67
|
+
This property sets the internal dictionary about the original instantiated features of this
|
|
68
|
+
:class:`Device`/:class:`VDevice`. This is done, to ensure that balder has saved an original copy of the original
|
|
69
|
+
instantiated abstract features. The real features will be overwritten for each new variation by the
|
|
70
|
+
:class:`ExecutorTree`!
|
|
71
|
+
"""
|
|
72
|
+
self._original_instanced_features = self.get_all_instantiated_feature_objects()
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__file__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Controller(ABC):
|
|
10
|
+
"""
|
|
11
|
+
This is the base controller class. It serves as a base class for all other specialized controllers.
|
|
12
|
+
|
|
13
|
+
A controller is used to manage the behavior internally.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
@abstractmethod
|
|
18
|
+
def get_for(related_cls):
|
|
19
|
+
"""
|
|
20
|
+
This class returns the current existing controller instance for the given item. If the instance does not exist
|
|
21
|
+
yet, it will automatically create it and saves the instance in an internal dictionary.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
@abstractmethod
|
|
26
|
+
def related_cls(self):
|
|
27
|
+
"""
|
|
28
|
+
This method returns the related class that belongs to that controller.
|
|
29
|
+
"""
|