baldertest 0.1.0b9__py3-none-any.whl → 0.1.0b11__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 (53) hide show
  1. _balder/_version.py +1 -1
  2. _balder/balder_session.py +4 -2
  3. _balder/cnnrelations/__init__.py +7 -0
  4. _balder/cnnrelations/and_connection_relation.py +149 -0
  5. _balder/cnnrelations/base_connection_relation.py +270 -0
  6. _balder/cnnrelations/or_connection_relation.py +65 -0
  7. _balder/collector.py +116 -14
  8. _balder/connection.py +400 -881
  9. _balder/connection_metadata.py +255 -0
  10. _balder/console/balder.py +1 -1
  11. _balder/controllers/device_controller.py +26 -12
  12. _balder/controllers/feature_controller.py +63 -99
  13. _balder/controllers/normal_scenario_setup_controller.py +7 -7
  14. _balder/controllers/scenario_controller.py +97 -7
  15. _balder/controllers/setup_controller.py +2 -3
  16. _balder/decorator_connect.py +12 -10
  17. _balder/decorator_fixture.py +4 -7
  18. _balder/decorator_for_vdevice.py +21 -31
  19. _balder/decorator_gateway.py +3 -3
  20. _balder/decorator_parametrize.py +31 -0
  21. _balder/decorator_parametrize_by_feature.py +36 -0
  22. _balder/exceptions.py +6 -0
  23. _balder/executor/basic_executable_executor.py +126 -0
  24. _balder/executor/basic_executor.py +1 -78
  25. _balder/executor/executor_tree.py +7 -5
  26. _balder/executor/parametrized_testcase_executor.py +52 -0
  27. _balder/executor/scenario_executor.py +5 -2
  28. _balder/executor/setup_executor.py +5 -2
  29. _balder/executor/testcase_executor.py +41 -9
  30. _balder/executor/unresolved_parametrized_testcase_executor.py +209 -0
  31. _balder/executor/variation_executor.py +148 -123
  32. _balder/feature.py +1 -1
  33. _balder/fixture_definition_scope.py +19 -0
  34. _balder/fixture_execution_level.py +22 -0
  35. _balder/fixture_manager.py +170 -182
  36. _balder/fixture_metadata.py +26 -0
  37. _balder/objects/connections/osi_3_network.py +2 -2
  38. _balder/objects/connections/osi_4_transport.py +2 -2
  39. _balder/parametrization.py +75 -0
  40. _balder/routing_path.py +18 -25
  41. _balder/solver.py +52 -32
  42. _balder/testresult.py +1 -1
  43. _balder/utils.py +27 -1
  44. balder/__init__.py +6 -0
  45. balder/exceptions.py +4 -3
  46. balder/parametrization.py +8 -0
  47. {baldertest-0.1.0b9.dist-info → baldertest-0.1.0b11.dist-info}/METADATA +2 -2
  48. baldertest-0.1.0b11.dist-info/RECORD +83 -0
  49. {baldertest-0.1.0b9.dist-info → baldertest-0.1.0b11.dist-info}/WHEEL +1 -1
  50. baldertest-0.1.0b9.dist-info/RECORD +0 -68
  51. {baldertest-0.1.0b9.dist-info → baldertest-0.1.0b11.dist-info}/LICENSE +0 -0
  52. {baldertest-0.1.0b9.dist-info → baldertest-0.1.0b11.dist-info}/entry_points.txt +0 -0
  53. {baldertest-0.1.0b9.dist-info → baldertest-0.1.0b11.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,16 @@
1
1
  from __future__ import annotations
2
- from typing import List, Tuple, Generator, Dict, Union, Type, Callable, TYPE_CHECKING
2
+
3
+ import itertools
4
+ from typing import List, Tuple, Generator, Dict, Union, Type, Callable, Iterable, TYPE_CHECKING
3
5
 
4
6
  import inspect
5
7
  from graphlib import TopologicalSorter
6
8
  from _balder.executor.testcase_executor import TestcaseExecutor
7
9
  from _balder.scenario import Scenario
8
10
  from _balder.setup import Setup
11
+ from _balder.fixture_definition_scope import FixtureDefinitionScope
12
+ from _balder.fixture_execution_level import FixtureExecutionLevel
13
+ from _balder.fixture_metadata import FixtureMetadata
9
14
  from _balder.executor.basic_executor import BasicExecutor
10
15
  from _balder.executor.setup_executor import SetupExecutor
11
16
  from _balder.executor.scenario_executor import ScenarioExecutor
@@ -22,22 +27,22 @@ class FixtureManager:
22
27
  """
23
28
  This class is the global fixture manager. It provides various methods to manage fixtures in the balder system.
24
29
  """
25
- #: the ordering for the execution levels
26
- EXECUTION_LEVEL_ORDER = ['session', 'setup', 'scenario', 'variation', 'testcase']
27
30
 
28
- def __init__(self, fixtures: Dict[str, Dict[Union[type, None], List[Tuple[MethodLiteralType, Callable]]]]):
31
+ def __init__(
32
+ self,
33
+ fixtures: Dict[FixtureExecutionLevel,
34
+ Dict[Union[None, Type[Scenario], Type[Setup]], List[Tuple[MethodLiteralType, Callable]]]]):
29
35
 
30
- # The first key is the fixture level, the second key is the namespace in which the fixture is defined (for
31
- # example the scenario class), which describes the definition-scope. As value a list with tuples is returned.
32
- # The first element is the type of the method/function and the second is the callable itself.
33
- self.fixtures: Dict[str, Dict[Union[type, None], List[Tuple[MethodLiteralType, Callable]]]] = fixtures
36
+ # The first key is the fixture level, the second key is the namespace in which the fixture is defined. As value
37
+ # a list with tuples is returned. The first element is the type of the method/function and the second is the
38
+ # callable itself.
39
+ self.fixtures: Dict[FixtureExecutionLevel,
40
+ Dict[Union[None, Type[Scenario], Type[Setup]], List[Tuple[MethodLiteralType, Callable]]]] \
41
+ = fixtures
34
42
 
35
- # contains all active fixtures with their namespace, their func_type, their callable, the generator object
36
- # (otherwise an empty generator, if the fixture is not a generator) and the result according to the fixture's
37
- # construction code (will be cleaned after it leaves a level)
38
- self.current_tree_fixtures: \
39
- Dict[str, List[
40
- Tuple[Union[Type[ExecutorTree], Type[Setup], Type[Scenario]], str, Callable, Generator, object]]] = {}
43
+ # contains all active fixtures with their namespace, their func_type, their callable, the generator object and
44
+ # the result according to the fixture's construction code (will be cleaned after it leaves a level)
45
+ self.current_tree_fixtures: Dict[FixtureExecutionLevel, List[FixtureMetadata]] = {}
41
46
 
42
47
  # ---------------------------------- STATIC METHODS ----------------------------------------------------------------
43
48
 
@@ -45,128 +50,23 @@ class FixtureManager:
45
50
 
46
51
  # ---------------------------------- PROPERTIES --------------------------------------------------------------------
47
52
 
48
- @property
49
- def resolve_type_level(self):
50
- """
51
- returns a dictionary that holds the executor class as key and the fixture definition that belongs to this
52
- executor class as key
53
- """
54
- from _balder.executor.executor_tree import ExecutorTree
55
-
56
- return {
57
- ExecutorTree: 'session',
58
- SetupExecutor: 'setup',
59
- ScenarioExecutor: 'scenario',
60
- VariationExecutor: 'variation',
61
- TestcaseExecutor: 'testcase'
62
- }
63
-
64
- @property
65
- def definition_scope_order(self):
66
- """
67
- returns a list with the definition scope objects in the priority order (ExecutorTree stands for global fixtures)
68
- """
69
- from _balder.executor.executor_tree import ExecutorTree
70
- return [ExecutorTree, Setup, Scenario]
71
-
72
53
  @property
73
54
  def all_already_run_fixtures(self) -> List[Callable]:
74
55
  """
75
56
  returns a list of all fixtures that have already been run
76
57
  """
77
58
  complete_list_in_order = []
78
- for cur_level in self.EXECUTION_LEVEL_ORDER:
59
+ for cur_level in FixtureExecutionLevel:
79
60
  if cur_level in self.current_tree_fixtures.keys():
80
61
  complete_list_in_order += [
81
- cur_fixture for _, _, cur_fixture, _, _ in self.current_tree_fixtures[cur_level]]
62
+ cur_fixture_metadata.callable for cur_fixture_metadata in self.current_tree_fixtures[cur_level]]
82
63
  return complete_list_in_order
83
64
 
84
65
  # ---------------------------------- PROTECTED METHODS -------------------------------------------------------------
85
66
 
86
- def get_all_attribute_values(
87
- self, branch: Union[ExecutorTree, SetupExecutor, ScenarioExecutor, VariationExecutor, TestcaseExecutor],
88
- callable_func_namespace: Union[Type[ExecutorTree], Type[Scenario], Type[Setup]], callable_func: Callable,
89
- func_type: str) -> Dict[str, object]:
90
- """
91
- This method tries to fill the unresolved function/method arguments of the given fixture callable. For this it
92
- searches the return values of all already executed fixtures and supplies the argument values of the
93
- given ``fixture`` callable in a dictionary.
94
-
95
- It automatically manages `self` / `cls` references for func_type `instancemethod` or `classmethod`. It
96
- only returns the fixture references and ignores `self` / `cls`
97
-
98
- First the method tries to find the arguments in all fixtures that are in the same namespace. If it does not find
99
- any references, it will look in the next higher scope. It only uses fixture that has run before!
100
-
101
- :param branch: the current active branch
102
-
103
- :param callable_func_namespace: the namespace of the current fixture
104
-
105
- :param callable_func: the callable with the arguments
106
-
107
- :param func_type: returns the func_type of the fixture - depending on this value the first argument will be
108
- ignored, because it has to be `cls` for `classmethod` and `self` for `instancemethod`
109
-
110
- :return: the method returns a dictionary with the attribute name as key and the return value as value
111
- """
112
- from _balder.executor.executor_tree import ExecutorTree
113
-
114
- arguments = inspect.getfullargspec(callable_func).args
115
- result_dict = {}
116
-
117
- if func_type in ["classmethod", "instancemethod"]:
118
- arguments = arguments[1:]
119
-
120
- self._validate_for_unclear_setup_scoped_fixture_reference(
121
- callable_func_namespace, callable_func, arguments,
122
- cur_execution_level=self.resolve_type_level[branch.__class__])
123
- all_possible_namespaces = [ExecutorTree]
124
- # determine namespaces (in the correct order)
125
- setup_type = None
126
- scenario_type = None
127
- if isinstance(branch, SetupExecutor):
128
- setup_type = branch.base_setup_class.__class__
129
- # normally the scenario is None - only if the current namespace is a scenario we can use it
130
- if issubclass(callable_func_namespace, Scenario):
131
- scenario_type = callable_func_namespace
132
- else:
133
- scenario_type = None
134
- elif isinstance(branch, ScenarioExecutor):
135
- setup_type = branch.parent_executor.base_setup_class.__class__
136
- scenario_type = branch.base_scenario_class.__class__
137
- elif isinstance(branch, VariationExecutor):
138
- setup_type = branch.cur_setup_class.__class__
139
- scenario_type = branch.cur_scenario_class.__class__
140
- elif isinstance(branch, TestcaseExecutor):
141
- setup_type = branch.parent_executor.cur_setup_class.__class__
142
- scenario_type = branch.parent_executor.cur_scenario_class.__class__
143
-
144
- # add to possible namespaces only if the namespace of the current fixture allows this
145
- if (issubclass(callable_func_namespace, Setup) or issubclass(callable_func_namespace, Scenario)) \
146
- and setup_type is not None:
147
- all_possible_namespaces.append(setup_type)
148
- if issubclass(callable_func_namespace, Scenario) and scenario_type is not None:
149
- all_possible_namespaces.append(scenario_type)
150
-
151
- for cur_arg in arguments:
152
- # go to the most specific fixture, because more specific ones overwrite the more global ones
153
- for cur_possible_namespace in all_possible_namespaces:
154
- for cur_level in self.EXECUTION_LEVEL_ORDER:
155
- if cur_level in self.current_tree_fixtures.keys():
156
- # filter only these fixtures that have the same namespace
157
- for cur_fixt_namespace, _, cur_fixt, _, cur_fixt_retval in \
158
- self.current_tree_fixtures[cur_level]:
159
- if cur_fixt_namespace == cur_possible_namespace:
160
- if cur_fixt.__name__ == cur_arg:
161
- result_dict[cur_arg] = cur_fixt_retval
162
- if cur_arg not in result_dict.keys():
163
- raise FixtureReferenceError(
164
- f"the argument `{cur_arg}` in fixture `{callable_func.__qualname__}` could not be resolved")
165
- return result_dict
166
-
167
67
  def _validate_for_unclear_setup_scoped_fixture_reference(
168
- self, fixture_callable_namespace: Union[Type[ExecutorTree], Type[Scenario], Type[Setup]],
169
- fixture_callable: Callable, arguments: List[str], cur_execution_level: str):
68
+ self, fixture_callable_namespace: Union[None, Type[Scenario], Type[Setup]],
69
+ fixture_callable: Callable, arguments: List[str], cur_execution_level: FixtureExecutionLevel):
170
70
  """
171
71
  This helper method validates a given fixture reference for the unclear setup scoped fixture reference.
172
72
 
@@ -184,7 +84,7 @@ class FixtureManager:
184
84
  The error will be thrown if a scenario scoped fixture with the execution level SESSION references a fixture
185
85
  name that exists in one or more setup scoped fixtures with the execution level SESSION.
186
86
 
187
- :param fixture_callable_namespace: the namespace of the current fixture
87
+ :param fixture_callable_namespace: the namespace of the current fixture or None if it is a `balderglob.py` file
188
88
 
189
89
  :param fixture_callable: the callable with the arguments
190
90
 
@@ -194,10 +94,11 @@ class FixtureManager:
194
94
  """
195
95
  # this method is only interested in fixtures with execution level SESSION!
196
96
 
197
- if isinstance(fixture_callable_namespace, Scenario) and cur_execution_level == "session":
97
+ if (fixture_callable_namespace is not None and isinstance(fixture_callable_namespace, Scenario)
98
+ and cur_execution_level == FixtureExecutionLevel.SESSION):
198
99
  all_setup_scoped_fixtures = []
199
100
 
200
- for cur_namespace_type, fixtures in self.fixtures.get('session', {}).items():
101
+ for cur_namespace_type, fixtures in self.fixtures.get(FixtureExecutionLevel.SESSION, {}).items():
201
102
  if cur_namespace_type is not None and issubclass(cur_namespace_type, Setup):
202
103
  all_setup_scoped_fixtures += [cur_fixt.__name__ for _, cur_fixt in fixtures]
203
104
  for cur_arg in arguments:
@@ -208,9 +109,9 @@ class FixtureManager:
208
109
  f"fixtures with the definition scope that have the same name - please rename them!")
209
110
 
210
111
  def _sort_fixture_list_of_same_definition_scope(
211
- self, fixture_namespace_dict: Dict[Tuple[str, Union[Type[ExecutorTree], Type[Scenario], Type[Setup]]],
212
- List[object]], outer_scope_fixtures: List[object]) \
213
- -> List[Tuple[Union[Type[ExecutorTree], Type[Scenario], Type[Setup]], str, Callable]]:
112
+ self, fixture_namespace_dict: Dict[Union[None, Type[Scenario], Type[Setup]], List[object]],
113
+ outer_scope_fixtures: List[object]) \
114
+ -> List[Tuple[Union[None, Type[Scenario], Type[Setup]], str, Callable]]:
214
115
  """
215
116
  This is a helper method that allows to sort the fixtures given in `fixture_namespace_dict`, depending on their
216
117
  arguments.
@@ -233,7 +134,7 @@ class FixtureManager:
233
134
 
234
135
  # returns the func_type for the given fixture
235
136
  fixture_func_types = {}
236
- for cur_namespace_type, cur_fixture_list in fixture_namespace_dict.items():
137
+ for _, cur_fixture_list in fixture_namespace_dict.items():
237
138
  for cur_func_type, cur_fixture in cur_fixture_list:
238
139
  fixture_func_types[cur_fixture] = cur_func_type
239
140
 
@@ -279,6 +180,39 @@ class FixtureManager:
279
180
  return [(cur_definition_scope, fixture_func_types[cur_func], cur_func)
280
181
  for cur_definition_scope, cur_func in sorter.static_order()]
281
182
 
183
+ def _determine_setup_and_scenario_type(
184
+ self,
185
+ from_branch: Union[ExecutorTree, SetupExecutor, ScenarioExecutor, VariationExecutor, TestcaseExecutor],
186
+ callable_func_namespace: Union[None, Type[Scenario], Type[Setup]]) \
187
+ -> Tuple[Union[None, Type[Setup]], Union[None, Type[Scenario]]]:
188
+ """
189
+ determines the setup and scenario type for a specific fixture based on the branch the system is in
190
+
191
+ :param from_branch: the branch for which the types should be determined
192
+ :param callable_func_namespace: the namespace of the current fixture or `None` if it is defined in balderglob
193
+ file
194
+ """
195
+ # determine namespaces (in the correct order)
196
+ setup_type = None
197
+ scenario_type = None
198
+ if isinstance(from_branch, SetupExecutor):
199
+ setup_type = from_branch.base_setup_class.__class__
200
+ # normally the scenario is None - only if the current namespace is a scenario we can use it
201
+ if callable_func_namespace is not None and issubclass(callable_func_namespace, Scenario):
202
+ scenario_type = callable_func_namespace
203
+ else:
204
+ scenario_type = None
205
+ elif isinstance(from_branch, ScenarioExecutor):
206
+ setup_type = from_branch.parent_executor.base_setup_class.__class__
207
+ scenario_type = from_branch.base_scenario_class.__class__
208
+ elif isinstance(from_branch, VariationExecutor):
209
+ setup_type = from_branch.cur_setup_class.__class__
210
+ scenario_type = from_branch.cur_scenario_class.__class__
211
+ elif isinstance(from_branch, TestcaseExecutor):
212
+ setup_type = from_branch.parent_executor.cur_setup_class.__class__
213
+ scenario_type = from_branch.parent_executor.cur_scenario_class.__class__
214
+ return setup_type, scenario_type
215
+
282
216
  # ---------------------------------- METHODS -----------------------------------------------------------------------
283
217
 
284
218
  def is_allowed_to_enter(
@@ -287,8 +221,7 @@ class FixtureManager:
287
221
  """
288
222
  This method return true if the given branch can be entered, otherwise false
289
223
  """
290
- execution_level = self.resolve_type_level[branch.__class__]
291
- return execution_level not in self.current_tree_fixtures.keys()
224
+ return branch.fixture_execution_level not in self.current_tree_fixtures.keys()
292
225
 
293
226
  def is_allowed_to_leave(
294
227
  self, branch: Union[BasicExecutor, ExecutorTree, SetupExecutor, ScenarioExecutor, VariationExecutor,
@@ -298,8 +231,7 @@ class FixtureManager:
298
231
  This method returns true if the given branch can be left now (there exist entries from earlier run enter()
299
232
  for this branch), otherwise false
300
233
  """
301
- execution_level = self.resolve_type_level[branch.__class__]
302
- return execution_level in self.current_tree_fixtures.keys()
234
+ return branch.fixture_execution_level in self.current_tree_fixtures.keys()
303
235
 
304
236
  def enter(self, branch: Union[BasicExecutor, ExecutorTree, SetupExecutor, ScenarioExecutor, VariationExecutor,
305
237
  TestcaseExecutor]):
@@ -311,7 +243,6 @@ class FixtureManager:
311
243
 
312
244
  :raise BalderFixtureException: is thrown if an error occurs while executing a user fixture
313
245
  """
314
- execution_level = self.resolve_type_level[branch.__class__]
315
246
 
316
247
  if not self.is_allowed_to_enter(branch):
317
248
  raise LostInExecutorTreeException(
@@ -321,7 +252,7 @@ class FixtureManager:
321
252
  yield None
322
253
  # now iterate over all fixtures that should be executed in this enter() call
323
254
  # -> collect them with all different DEFINITION-SCOPES
324
- for cur_definition_scope in self.definition_scope_order:
255
+ for cur_definition_scope in FixtureDefinitionScope:
325
256
  cur_fixture_list = self.get_all_fixtures_for_current_level(branch=branch).get(cur_definition_scope)
326
257
  for cur_scope_namespace_type, cur_fixture_func_type, cur_fixture in cur_fixture_list:
327
258
  try:
@@ -354,10 +285,11 @@ class FixtureManager:
354
285
  cur_generator = empty()
355
286
  next(cur_generator)
356
287
  # add the executed fixtures to global reference
357
- if execution_level not in self.current_tree_fixtures.keys():
358
- self.current_tree_fixtures[execution_level] = []
359
- self.current_tree_fixtures[execution_level].append(
360
- (cur_scope_namespace_type, cur_fixture_func_type, cur_fixture, cur_generator, cur_retvalue))
288
+ if branch.fixture_execution_level not in self.current_tree_fixtures.keys():
289
+ self.current_tree_fixtures[branch.fixture_execution_level] = []
290
+ self.current_tree_fixtures[branch.fixture_execution_level].append(
291
+ FixtureMetadata(namespace=cur_scope_namespace_type, function_type=cur_fixture_func_type,
292
+ callable=cur_fixture, generator=cur_generator, retval=cur_retvalue))
361
293
  except StopIteration:
362
294
  pass
363
295
  # every other exception that is thrown, will be recognized and rethrown
@@ -371,36 +303,95 @@ class FixtureManager:
371
303
  :param branch: specifies the element of the ExecutorTree that should be left (note that the current position
372
304
  is very important here)
373
305
  """
374
- execution_level = self.resolve_type_level[branch.__class__]
375
- if execution_level not in self.current_tree_fixtures.keys():
306
+ if branch.fixture_execution_level not in self.current_tree_fixtures.keys():
376
307
  raise LostInExecutorTreeException("can not leave the current branch, because it was not entered before")
377
308
 
378
- current_tree_fixtures_reversed = self.current_tree_fixtures[execution_level]
309
+ current_tree_fixtures_reversed = self.current_tree_fixtures[branch.fixture_execution_level]
379
310
  current_tree_fixtures_reversed.reverse()
380
311
  exception = None
381
- for _, _, _, cur_generator, _ in current_tree_fixtures_reversed:
312
+ for cur_fixture_metadata in current_tree_fixtures_reversed:
382
313
  try:
383
- next(cur_generator)
314
+ next(cur_fixture_metadata.generator)
384
315
  except StopIteration:
385
316
  pass
386
- except Exception as exc:
317
+ except Exception as exc: # pylint: disable=broad-exception-caught
387
318
  if not exception:
388
319
  # only save the first exception
389
320
  exception = exc
390
321
 
391
322
  # reset the left location
392
- del self.current_tree_fixtures[execution_level]
323
+ del self.current_tree_fixtures[branch.fixture_execution_level]
393
324
 
394
325
  if exception:
395
326
  raise exception
396
327
 
397
- def get_fixture_for_class(
398
- self,
399
- execution_level: Union[Type[ExecutorTree], Type[SetupExecutor], Type[ScenarioExecutor],
400
- Type[VariationExecutor], Type[TestcaseExecutor]],
401
- setup_or_scenario_class: Union[None, Type[Setup], Type[Scenario]],
402
- parent_classes: bool = True
403
- ) -> List[Tuple[MethodLiteralType, Callable]]:
328
+ def get_all_attribute_values(
329
+ self, branch: Union[ExecutorTree, SetupExecutor, ScenarioExecutor, VariationExecutor, TestcaseExecutor],
330
+ callable_func_namespace: Union[None, Type[Scenario], Type[Setup]], callable_func: Callable,
331
+ func_type: str, ignore_attributes: Iterable[str] = None) -> Dict[str, object]:
332
+ """
333
+ This method tries to fill the unresolved function/method arguments of the given fixture callable. For this it
334
+ searches the return values of all already executed fixtures and supplies the argument values of the
335
+ given ``fixture`` callable in a dictionary.
336
+
337
+ It automatically manages `self` / `cls` references for func_type `instancemethod` or `classmethod`. It
338
+ only returns the fixture references and ignores `self` / `cls`
339
+
340
+ First the method tries to find the arguments in all fixtures that are in the same namespace. If it does not find
341
+ any references, it will look in the next higher scope. It only uses fixture that has run before!
342
+
343
+ :param branch: the current active branch
344
+ :param callable_func_namespace: the namespace of the current fixture or `None` if it is defined in balderglob
345
+ file
346
+ :param callable_func: the callable with the arguments
347
+ :param func_type: returns the func_type of the fixture - depending on this value the first argument will be
348
+ ignored, because it has to be `cls` for `classmethod` and `self` for `instancemethod`
349
+ :param ignore_attributes: holds a list of attributes in the test method that should be ignored
350
+ :return: the method returns a dictionary with the attribute name as key and the return value as value
351
+
352
+ """
353
+ arguments = inspect.getfullargspec(callable_func).args
354
+ result_dict = {}
355
+
356
+ if func_type in ["classmethod", "instancemethod"]:
357
+ arguments = arguments[1:]
358
+ if ignore_attributes is None:
359
+ ignore_attributes = []
360
+
361
+ self._validate_for_unclear_setup_scoped_fixture_reference(
362
+ callable_func_namespace, callable_func, arguments, cur_execution_level=branch.fixture_execution_level)
363
+ all_possible_namespaces = [None]
364
+ setup_type, scenario_type = self._determine_setup_and_scenario_type(
365
+ from_branch=branch, callable_func_namespace=callable_func_namespace)
366
+
367
+ # add to possible namespaces only if the namespace of the current fixture allows this
368
+ if callable_func_namespace is not None:
369
+ if (issubclass(callable_func_namespace, Setup) or issubclass(callable_func_namespace, Scenario)) \
370
+ and setup_type is not None:
371
+ all_possible_namespaces.append(setup_type)
372
+ if issubclass(callable_func_namespace, Scenario) and scenario_type is not None:
373
+ all_possible_namespaces.append(scenario_type)
374
+
375
+ for cur_arg in arguments:
376
+ if cur_arg in ignore_attributes:
377
+ continue
378
+ # go to the most specific fixture, because more specific ones overwrite the more global ones
379
+ for cur_possible_namespace, cur_level in itertools.product(all_possible_namespaces, FixtureExecutionLevel):
380
+ if cur_level not in self.current_tree_fixtures.keys():
381
+ continue
382
+ # filter only these fixtures that have the same namespace
383
+ for cur_fixture_metadata in self.current_tree_fixtures[cur_level]:
384
+ if (cur_fixture_metadata.namespace == cur_possible_namespace
385
+ and cur_fixture_metadata.callable.__name__ == cur_arg):
386
+ result_dict[cur_arg] = cur_fixture_metadata.retval
387
+ if cur_arg not in result_dict.keys():
388
+ raise FixtureReferenceError(
389
+ f"the argument `{cur_arg}` in fixture `{callable_func.__qualname__}` could not be resolved")
390
+ return result_dict
391
+
392
+ def get_fixture_for_class(self, execution_level: FixtureExecutionLevel,
393
+ setup_or_scenario_class: Union[None, Type[Setup], Type[Scenario]],
394
+ parent_classes: bool = True) -> List[Tuple[MethodLiteralType, Callable]]:
404
395
  """
405
396
  This method returns all classes of a specific Setup/Scenario class for a specific execution-level.
406
397
 
@@ -409,10 +400,9 @@ class FixtureManager:
409
400
  :param parent_classes: true if the method should look for fixtures in parent classes too
410
401
  :return: list with all fixtures that are matching search criteria
411
402
  """
412
- # current relevant EXECUTION LEVEL - all other levels are not relevant for this call
413
- cur_execution_level = self.resolve_type_level[execution_level]
414
- # get all fixtures of the current relevant level
415
- fixtures_of_exec_level = self.fixtures.get(cur_execution_level, {})
403
+ # get all fixtures of the current relevant level (only `execution_level` is relevant - all other levels are
404
+ # not relevant for this call)
405
+ fixtures_of_exec_level = self.fixtures.get(execution_level, {})
416
406
  if setup_or_scenario_class is not None and parent_classes:
417
407
  all_fixtures = []
418
408
  for cur_parent_class in inspect.getmro(setup_or_scenario_class):
@@ -426,13 +416,11 @@ class FixtureManager:
426
416
  _added_fixtures.append(cur_fixture_tuple[1].__name__)
427
417
  remaining_fixtures.append(cur_fixture_tuple)
428
418
  return remaining_fixtures
429
- else:
430
- return fixtures_of_exec_level.get(setup_or_scenario_class, [])
419
+ return fixtures_of_exec_level.get(setup_or_scenario_class, [])
431
420
 
432
421
  def get_all_fixtures_for_current_level(
433
422
  self, branch: Union[ExecutorTree, SetupExecutor, ScenarioExecutor, VariationExecutor, TestcaseExecutor]) \
434
- -> Dict[Union[Type[ExecutorTree], Type[Scenario], Type[Setup]],
435
- List[Tuple[Union[Type[ExecutorTree], Type[Scenario], Type[Setup]], str, object]]]:
423
+ -> Dict[FixtureDefinitionScope, List[Tuple[Union[None, Type[Scenario], Type[Setup]], str, object]]]:
436
424
  """
437
425
  This method delivers all fixtures which should be executed for the given branch of the executor tree.
438
426
 
@@ -450,46 +438,46 @@ class FixtureManager:
450
438
  or :class:`Setup`) as first argument, the fixture func_type as second and the fixture callable as third
451
439
  argument (this list is ordered after the call hierarchy)
452
440
  """
453
- from _balder.executor.executor_tree import ExecutorTree
454
-
455
441
  all_fixtures = {}
456
442
  # get all relevant fixtures of `balderglob.py` (None is key for balderglob fixtures)
457
- glob_fixtures = self.get_fixture_for_class(branch.__class__, None)
458
- all_fixtures[ExecutorTree] = {}
459
- all_fixtures[ExecutorTree][ExecutorTree] = glob_fixtures
443
+ glob_fixtures = self.get_fixture_for_class(branch.fixture_execution_level, None)
444
+ all_fixtures[FixtureDefinitionScope.GLOB] = {}
445
+ all_fixtures[FixtureDefinitionScope.GLOB][None] = glob_fixtures
460
446
  # get all relevant fixtures with definition scope "setup"
461
- all_fixtures[Setup] = {}
447
+ all_fixtures[FixtureDefinitionScope.SETUP] = {}
462
448
  for cur_setup in branch.get_all_base_instances_of_this_branch(Setup, only_runnable_elements=True):
463
449
  # check if there exists fixtures for the current setup
464
- cur_setup_fixtures = self.get_fixture_for_class(branch.__class__, cur_setup.__class__)
450
+ cur_setup_fixtures = self.get_fixture_for_class(branch.fixture_execution_level, cur_setup.__class__)
465
451
  if cur_setup_fixtures:
466
- all_fixtures[Setup][cur_setup.__class__] = cur_setup_fixtures
452
+ all_fixtures[FixtureDefinitionScope.SETUP][cur_setup.__class__] = cur_setup_fixtures
467
453
 
468
454
  # get all relevant fixtures with definition scope "scenario"
469
- all_fixtures[Scenario] = {}
455
+ all_fixtures[FixtureDefinitionScope.SCENARIO] = {}
470
456
  for cur_scenario in branch.get_all_base_instances_of_this_branch(Scenario, only_runnable_elements=True):
471
- cur_scenario_fixtures = self.get_fixture_for_class(branch.__class__, cur_scenario.__class__)
457
+ cur_scenario_fixtures = self.get_fixture_for_class(branch.fixture_execution_level, cur_scenario.__class__)
472
458
  if cur_scenario_fixtures:
473
- all_fixtures[Scenario][cur_scenario.__class__] = cur_scenario_fixtures
459
+ all_fixtures[FixtureDefinitionScope.SCENARIO][cur_scenario.__class__] = cur_scenario_fixtures
474
460
 
475
461
  ordered_fixtures = {}
476
462
  # Now the basic order is: [All of ExecutorTree] -> [All of Setup] -> [All of Scenario]
477
463
  # but the order within these DEFINITION SCOPES has to be determined now!
478
464
  outer_scope_fixtures = self.all_already_run_fixtures
479
- ordered_fixtures[ExecutorTree] = self._sort_fixture_list_of_same_definition_scope(
480
- fixture_namespace_dict=all_fixtures[ExecutorTree], outer_scope_fixtures=outer_scope_fixtures)
465
+ ordered_fixtures[FixtureDefinitionScope.GLOB] = self._sort_fixture_list_of_same_definition_scope(
466
+ fixture_namespace_dict=all_fixtures[FixtureDefinitionScope.GLOB], outer_scope_fixtures=outer_scope_fixtures)
481
467
 
482
468
  outer_scope_fixtures = \
483
469
  self.all_already_run_fixtures + \
484
- [cur_fixture for _, _, cur_fixture in ordered_fixtures[ExecutorTree]]
485
- ordered_fixtures[Setup] = self._sort_fixture_list_of_same_definition_scope(
486
- fixture_namespace_dict=all_fixtures[Setup], outer_scope_fixtures=outer_scope_fixtures)
470
+ [cur_fixture for _, _, cur_fixture in ordered_fixtures[FixtureDefinitionScope.GLOB]]
471
+ ordered_fixtures[FixtureDefinitionScope.SETUP] = self._sort_fixture_list_of_same_definition_scope(
472
+ fixture_namespace_dict=all_fixtures[FixtureDefinitionScope.SETUP],
473
+ outer_scope_fixtures=outer_scope_fixtures)
487
474
 
488
475
  outer_scope_fixtures = \
489
476
  self.all_already_run_fixtures + \
490
- [cur_fixture for _, _, cur_fixture in ordered_fixtures[ExecutorTree]] + \
491
- [cur_fixture for _, _, cur_fixture in ordered_fixtures[Setup]]
492
- ordered_fixtures[Scenario] = self._sort_fixture_list_of_same_definition_scope(
493
- fixture_namespace_dict=all_fixtures[Scenario], outer_scope_fixtures=outer_scope_fixtures)
477
+ [cur_fixture for _, _, cur_fixture in ordered_fixtures[FixtureDefinitionScope.GLOB]] + \
478
+ [cur_fixture for _, _, cur_fixture in ordered_fixtures[FixtureDefinitionScope.SETUP]]
479
+ ordered_fixtures[FixtureDefinitionScope.SCENARIO] = self._sort_fixture_list_of_same_definition_scope(
480
+ fixture_namespace_dict=all_fixtures[FixtureDefinitionScope.SCENARIO],
481
+ outer_scope_fixtures=outer_scope_fixtures)
494
482
 
495
483
  return ordered_fixtures
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+ from typing import Union, Type, Callable, Generator, TYPE_CHECKING
3
+ import dataclasses
4
+
5
+ from .utils import MethodLiteralType
6
+
7
+ if TYPE_CHECKING:
8
+ from _balder.scenario import Scenario
9
+ from _balder.setup import Setup
10
+
11
+
12
+ @dataclasses.dataclass
13
+ class FixtureMetadata:
14
+ """
15
+ describes meta information for a fixture
16
+ """
17
+ #: the namespace where it is defined (None if it is in a balderglob file)
18
+ namespace: Union[None, Type[Scenario], Type[Setup]]
19
+ #: the type of the fixture function
20
+ function_type: MethodLiteralType
21
+ #: the fixture callable itself
22
+ callable: Callable
23
+ #: the generator object (if the fixture is not a generator, it holds an empty generator)
24
+ generator: Generator
25
+ #: result according to the fixture's construction code (will be cleaned after it leaves a level)
26
+ retval: object
@@ -20,7 +20,7 @@ class IPv6Connection(Connection):
20
20
  """
21
21
 
22
22
 
23
- IPConnection = Connection.based_on(IPv4Connection, IPv6Connection)
23
+ IPConnection = Connection.based_on(IPv4Connection | IPv6Connection)
24
24
 
25
25
 
26
26
  @insert_into_tree(parents=[IPv4Connection, IPv6Connection])
@@ -44,4 +44,4 @@ class ICMPv6Connection(Connection):
44
44
  """
45
45
 
46
46
 
47
- ICMPConnection = Connection.based_on(ICMPv4Connection, ICMPv6Connection)
47
+ ICMPConnection = Connection.based_on(ICMPv4Connection | ICMPv6Connection)
@@ -20,7 +20,7 @@ class TcpIPv6Connection(Connection):
20
20
  """
21
21
 
22
22
 
23
- TcpConnection = Connection.based_on(TcpIPv4Connection, TcpIPv6Connection)
23
+ TcpConnection = Connection.based_on(TcpIPv4Connection | TcpIPv6Connection)
24
24
 
25
25
 
26
26
  @insert_into_tree(parents=[osi_3_network.IPv4Connection])
@@ -37,4 +37,4 @@ class UdpIPv6Connection(Connection):
37
37
  """
38
38
 
39
39
 
40
- UdpConnection = Connection.based_on(UdpIPv4Connection, UdpIPv6Connection)
40
+ UdpConnection = Connection.based_on(UdpIPv4Connection | UdpIPv6Connection)