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,270 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import List, Union, Type, Dict, TypeVar, TYPE_CHECKING
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from _balder.utils.functions import cnn_type_check_and_convert
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from ..connection import Connection
|
|
8
|
+
from ..connection import ConnectionMetadata
|
|
9
|
+
from ..device import Device
|
|
10
|
+
from .and_connection_relation import AndConnectionRelation
|
|
11
|
+
from .or_connection_relation import OrConnectionRelation
|
|
12
|
+
|
|
13
|
+
BaseConnectionRelationT = TypeVar('BaseConnectionRelationT', bound='BaseConnectionRelation')
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BaseConnectionRelation(ABC):
|
|
17
|
+
"""
|
|
18
|
+
This is the base connection list class, which provides the general functionality of connection collections.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, *connections: Union[Type[Connection], Connection, BaseConnectionRelationT]):
|
|
22
|
+
self._connections = []
|
|
23
|
+
# add it over append for type checking
|
|
24
|
+
for connection in connections:
|
|
25
|
+
self.append(connection)
|
|
26
|
+
|
|
27
|
+
def __iter__(self):
|
|
28
|
+
return self._connections.__iter__()
|
|
29
|
+
|
|
30
|
+
def __getitem__(self, item):
|
|
31
|
+
return self._connections.__getitem__(item)
|
|
32
|
+
|
|
33
|
+
def __len__(self):
|
|
34
|
+
return len(self._connections)
|
|
35
|
+
|
|
36
|
+
def __hash__(self):
|
|
37
|
+
all_hashes = 0
|
|
38
|
+
for cur_elem in self.connections:
|
|
39
|
+
all_hashes += hash(cur_elem) + hash(self.__class__.__name__)
|
|
40
|
+
return all_hashes
|
|
41
|
+
|
|
42
|
+
def __and__(
|
|
43
|
+
self,
|
|
44
|
+
other: Union[Connection, Type[Connection], AndConnectionRelation, OrConnectionRelation]
|
|
45
|
+
) -> AndConnectionRelation:
|
|
46
|
+
|
|
47
|
+
from .and_connection_relation import AndConnectionRelation # pylint: disable=import-outside-toplevel
|
|
48
|
+
|
|
49
|
+
new_list = AndConnectionRelation()
|
|
50
|
+
new_list.append(self)
|
|
51
|
+
new_list.append(cnn_type_check_and_convert(other))
|
|
52
|
+
return new_list
|
|
53
|
+
|
|
54
|
+
def __or__(
|
|
55
|
+
self,
|
|
56
|
+
other: Union[Connection, Type[Connection], AndConnectionRelation, OrConnectionRelation]
|
|
57
|
+
) -> OrConnectionRelation:
|
|
58
|
+
|
|
59
|
+
from .or_connection_relation import OrConnectionRelation # pylint: disable=import-outside-toplevel
|
|
60
|
+
|
|
61
|
+
new_list = OrConnectionRelation()
|
|
62
|
+
new_list.append(self)
|
|
63
|
+
new_list.append(cnn_type_check_and_convert(other))
|
|
64
|
+
return new_list
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def connections(self) -> List[Union[Connection, BaseConnectionRelationT]]:
|
|
68
|
+
"""
|
|
69
|
+
returns the components of this connection relation
|
|
70
|
+
"""
|
|
71
|
+
return self._connections.copy()
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def metadata(self) -> ConnectionMetadata | None:
|
|
75
|
+
"""
|
|
76
|
+
returns the metadata of this connection relation
|
|
77
|
+
"""
|
|
78
|
+
if not self.connections:
|
|
79
|
+
return None
|
|
80
|
+
|
|
81
|
+
# get all unique metadata objects
|
|
82
|
+
existing_metadata = list({elem.metadata for elem in self.connections if elem})
|
|
83
|
+
if len(existing_metadata) > 1:
|
|
84
|
+
raise ValueError(f'different metadata detected: `{existing_metadata}`')
|
|
85
|
+
return existing_metadata[0]
|
|
86
|
+
|
|
87
|
+
@abstractmethod
|
|
88
|
+
def get_simplified_relation(self) -> OrConnectionRelation:
|
|
89
|
+
"""
|
|
90
|
+
This method simplifies the connection relation. It will convert every possible relation into an
|
|
91
|
+
OrConnectionRelation[Connection, AndConnectionRelation].
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
def clone(self):
|
|
95
|
+
"""
|
|
96
|
+
clones this connection relation
|
|
97
|
+
"""
|
|
98
|
+
return self.__class__(*[cnn.clone() for cnn in self._connections])
|
|
99
|
+
|
|
100
|
+
def append(self, connection: Union[Type[Connection], Connection, BaseConnectionRelationT]):
|
|
101
|
+
"""
|
|
102
|
+
appends a component to this connection relation
|
|
103
|
+
"""
|
|
104
|
+
from ..connection import Connection # pylint: disable=import-outside-toplevel
|
|
105
|
+
|
|
106
|
+
if isinstance(connection, type):
|
|
107
|
+
if issubclass(connection, Connection):
|
|
108
|
+
connection = connection()
|
|
109
|
+
else:
|
|
110
|
+
raise TypeError(f'can not append a element `{connection.__name__}`')
|
|
111
|
+
if not isinstance(connection, Connection) and not isinstance(connection, BaseConnectionRelation):
|
|
112
|
+
raise TypeError('the element that should be appended to the relation needs to be a Connection or another '
|
|
113
|
+
'relation')
|
|
114
|
+
if isinstance(connection, self.__class__):
|
|
115
|
+
# directly add children (because it has the same type)
|
|
116
|
+
for cur_inner_connection in connection.connections:
|
|
117
|
+
self._connections.append(cur_inner_connection)
|
|
118
|
+
else:
|
|
119
|
+
self._connections.append(connection)
|
|
120
|
+
|
|
121
|
+
def extend(self, relation: BaseConnectionRelationT):
|
|
122
|
+
"""
|
|
123
|
+
extends the relation with the elements of provided relation
|
|
124
|
+
"""
|
|
125
|
+
if not isinstance(relation, self.__class__):
|
|
126
|
+
raise TypeError(f'the relation with the elements that should be appended needs to be from same type (is '
|
|
127
|
+
f'`{relation.__class__}` | expected `{self.__class__}`)')
|
|
128
|
+
for cur_elem in relation.connections:
|
|
129
|
+
self.append(cur_elem)
|
|
130
|
+
|
|
131
|
+
def set_metadata_for_all_subitems(self, metadata: Union[Dict[str, Union[Device, str]], None]):
|
|
132
|
+
"""
|
|
133
|
+
This method sets the metadata for all existing Connection items in this element.
|
|
134
|
+
|
|
135
|
+
:param metadata: the metadata that should be set (if the value is explicitly set to None, it removes the
|
|
136
|
+
metadata from every item)
|
|
137
|
+
"""
|
|
138
|
+
for cur_cnn in self._connections:
|
|
139
|
+
cur_cnn.set_metadata_for_all_subitems(metadata)
|
|
140
|
+
|
|
141
|
+
def get_all_used_connection_types(self) -> List[Type[Connection]]:
|
|
142
|
+
"""
|
|
143
|
+
This method returns all available connection types, that are used within this connection relation.
|
|
144
|
+
"""
|
|
145
|
+
from ..connection import Connection # pylint: disable=import-outside-toplevel
|
|
146
|
+
|
|
147
|
+
result = []
|
|
148
|
+
for cur_inner_elem in self._connections:
|
|
149
|
+
if isinstance(cur_inner_elem, Connection):
|
|
150
|
+
result.append(cur_inner_elem.__class__)
|
|
151
|
+
elif isinstance(cur_inner_elem, BaseConnectionRelation):
|
|
152
|
+
result.extend(cur_inner_elem.get_all_used_connection_types())
|
|
153
|
+
else:
|
|
154
|
+
raise TypeError(f'unexpected type for inner item `{cur_inner_elem.__class__.__name__}`')
|
|
155
|
+
return list(set(result))
|
|
156
|
+
|
|
157
|
+
@abstractmethod
|
|
158
|
+
def get_tree_str(self) -> str:
|
|
159
|
+
"""
|
|
160
|
+
returns the tree string for this part of the connection tree
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
def is_resolved(self) -> bool:
|
|
164
|
+
"""
|
|
165
|
+
returns whether this connection relation is part of a single connection
|
|
166
|
+
"""
|
|
167
|
+
if len(self._connections) == 0:
|
|
168
|
+
return True
|
|
169
|
+
return min(cnn.is_resolved() for cnn in self._connections)
|
|
170
|
+
|
|
171
|
+
def get_resolved(self) -> BaseConnectionRelationT:
|
|
172
|
+
"""
|
|
173
|
+
This method returns a resolved Connection Tree. This means that it convert the based_on references so that
|
|
174
|
+
every based on connection is a direct parent of the current element. It secures that there are no undefined
|
|
175
|
+
connection layers between an object and the given parent.
|
|
176
|
+
"""
|
|
177
|
+
return self.__class__(*[cnn.get_resolved() for cnn in self._connections])
|
|
178
|
+
|
|
179
|
+
@abstractmethod
|
|
180
|
+
def is_single(self) -> bool:
|
|
181
|
+
"""
|
|
182
|
+
returns whether this connection relation is part of a single connection
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
@abstractmethod
|
|
186
|
+
def get_singles(self) -> List[Connection]:
|
|
187
|
+
"""
|
|
188
|
+
returns the single connections of all components of this connection relation
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
def cnn_are_in_other(self, other: BaseConnectionRelationT, ignore_metadata: bool=False) -> bool:
|
|
192
|
+
"""
|
|
193
|
+
This method validates that the elements from this relation are contained in the other relation. Elements matches
|
|
194
|
+
with each other, as soon as they are equal.
|
|
195
|
+
|
|
196
|
+
.. note::
|
|
197
|
+
This method only checks that every single connections from the first element is contained in the second too.
|
|
198
|
+
It does not check the other direction. If you want to validate this, you need to call this method with both
|
|
199
|
+
possibilities.
|
|
200
|
+
|
|
201
|
+
:param other: the first list of connections
|
|
202
|
+
:param other: the other relation object
|
|
203
|
+
:param ignore_metadata: True, if the metadata of the single connections should be ignored
|
|
204
|
+
:return: True in case that every connection of the first list is equal with one in the second list, otherwise
|
|
205
|
+
False
|
|
206
|
+
"""
|
|
207
|
+
for cur_cnn in self._connections:
|
|
208
|
+
found_equal = False
|
|
209
|
+
for cur_other_cnn in other.connections:
|
|
210
|
+
if cur_cnn.equal_with(cur_other_cnn, ignore_metadata=ignore_metadata):
|
|
211
|
+
found_equal = True
|
|
212
|
+
break
|
|
213
|
+
if not found_equal:
|
|
214
|
+
return False
|
|
215
|
+
return True
|
|
216
|
+
|
|
217
|
+
@abstractmethod
|
|
218
|
+
def cut_into_all_possible_subtree_branches(self):
|
|
219
|
+
"""
|
|
220
|
+
This method returns a list of all possible connection tree branches. A branch is a single connection, while
|
|
221
|
+
this method returns a list of all possible singles where every single connection has this connection as head.
|
|
222
|
+
"""
|
|
223
|
+
|
|
224
|
+
def equal_with(self, other_relation: BaseConnectionRelationT, ignore_metadata=False) -> bool:
|
|
225
|
+
"""
|
|
226
|
+
This method returns True if the current relation matches with the other relation object. It always converts the
|
|
227
|
+
elements to a resolved version and checks if both of them are exactly the same.
|
|
228
|
+
|
|
229
|
+
.. note::
|
|
230
|
+
Note that both connection relations need to be resolved.
|
|
231
|
+
|
|
232
|
+
.. note::
|
|
233
|
+
Note that both Connection objects have to have the same ending parent elements. Only the order of the inner
|
|
234
|
+
connection elements are irrelevant.
|
|
235
|
+
|
|
236
|
+
:param other_relation: the other connection relation (needs to be the same type)
|
|
237
|
+
|
|
238
|
+
:param ignore_metadata: if this value is true the method ignores the metadata
|
|
239
|
+
|
|
240
|
+
:return: returns True if both elements are same
|
|
241
|
+
"""
|
|
242
|
+
if self.__class__ != other_relation.__class__:
|
|
243
|
+
return False
|
|
244
|
+
|
|
245
|
+
if not self.is_resolved():
|
|
246
|
+
raise ValueError('can not execute method, because connection relation is not resolved')
|
|
247
|
+
if not other_relation.is_resolved():
|
|
248
|
+
raise ValueError('can not execute method, because other connection relation is not resolved')
|
|
249
|
+
|
|
250
|
+
# check inner connection elements (if they match all in both directions)
|
|
251
|
+
return (self.cnn_are_in_other(other_relation, ignore_metadata=ignore_metadata)
|
|
252
|
+
and other_relation.cnn_are_in_other(self, ignore_metadata=ignore_metadata))
|
|
253
|
+
|
|
254
|
+
@abstractmethod
|
|
255
|
+
def contained_in(self, other_conn: Union[Connection, BaseConnectionRelationT], ignore_metadata=False) -> bool:
|
|
256
|
+
"""
|
|
257
|
+
This method helps to find out whether this connection relation fits within a given connection tree. A connection
|
|
258
|
+
object is a certain part of the large connection tree that Balder has at its disposal. This method checks
|
|
259
|
+
whether a possibility of this connection tree fits in one possibility of the given connection tree.
|
|
260
|
+
|
|
261
|
+
.. note::
|
|
262
|
+
The method returns true if one single connection of this object fits in another single connection that is
|
|
263
|
+
given by `other_conn`.
|
|
264
|
+
|
|
265
|
+
:param other_conn: the other connection
|
|
266
|
+
|
|
267
|
+
:param ignore_metadata: if this value is true the method ignores the metadata
|
|
268
|
+
|
|
269
|
+
:return: true if the self object is contained in the `other_conn`, otherwise false
|
|
270
|
+
"""
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import List, Union, TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
from .base_connection_relation import BaseConnectionRelation, BaseConnectionRelationT
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from ..connection import Connection
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class OrConnectionRelation(BaseConnectionRelation):
|
|
11
|
+
"""
|
|
12
|
+
describes a OR relation between connections
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def get_tree_str(self) -> str:
|
|
16
|
+
based_on_strings = [cur_elem.get_tree_str() for cur_elem in self._connections]
|
|
17
|
+
return f"({' & '.join(based_on_strings)})"
|
|
18
|
+
|
|
19
|
+
def get_simplified_relation(self) -> OrConnectionRelation:
|
|
20
|
+
from ..connection import Connection # pylint: disable=import-outside-toplevel
|
|
21
|
+
|
|
22
|
+
result = OrConnectionRelation()
|
|
23
|
+
for cur_inner_elem in self.connections:
|
|
24
|
+
if isinstance(cur_inner_elem, Connection):
|
|
25
|
+
# that is fine - just add it again
|
|
26
|
+
result.append(cur_inner_elem)
|
|
27
|
+
elif isinstance(cur_inner_elem, BaseConnectionRelation):
|
|
28
|
+
# simplify this AND/OR relation and add the items of it
|
|
29
|
+
# (the simplified version is always an OR relation!)
|
|
30
|
+
result.extend(cur_inner_elem.get_simplified_relation())
|
|
31
|
+
else:
|
|
32
|
+
raise TypeError(f'detect unexpected element type `{cur_inner_elem.__class__}` in inner elements')
|
|
33
|
+
return result
|
|
34
|
+
|
|
35
|
+
def is_single(self) -> bool:
|
|
36
|
+
if len(self.connections) == 0:
|
|
37
|
+
return True
|
|
38
|
+
if len(self.connections) == 1:
|
|
39
|
+
return self.connections[0].is_single()
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
def get_singles(self) -> List[Connection]:
|
|
43
|
+
result = []
|
|
44
|
+
for elem in self.connections:
|
|
45
|
+
result.extend(elem.get_singles())
|
|
46
|
+
return result
|
|
47
|
+
|
|
48
|
+
def cut_into_all_possible_subtree_branches(self) -> List[OrConnectionRelation]:
|
|
49
|
+
if not self.is_single():
|
|
50
|
+
raise ValueError('can not execute method, because relation is not single')
|
|
51
|
+
result = [OrConnectionRelation()]
|
|
52
|
+
if len(self.connections) == 0:
|
|
53
|
+
return result
|
|
54
|
+
result.extend(self.connections[0].cut_into_all_possible_subtree_branches())
|
|
55
|
+
return result
|
|
56
|
+
|
|
57
|
+
def contained_in(self, other_conn: Union[Connection, BaseConnectionRelationT], ignore_metadata=False) -> bool:
|
|
58
|
+
if not self.is_resolved():
|
|
59
|
+
raise ValueError('can not execute method, because connection relation is not resolved')
|
|
60
|
+
if not other_conn.is_resolved():
|
|
61
|
+
raise ValueError('can not execute method, because other connection relation is not resolved')
|
|
62
|
+
for cur_inner_cnn in self._connections:
|
|
63
|
+
if cur_inner_cnn.contained_in(other_conn, ignore_metadata=ignore_metadata):
|
|
64
|
+
return True
|
|
65
|
+
return False
|