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.
Files changed (89) hide show
  1. _balder/__init__.py +12 -0
  2. _balder/_version.py +34 -0
  3. _balder/balder_plugin.py +73 -0
  4. _balder/balder_session.py +341 -0
  5. _balder/balder_settings.py +15 -0
  6. _balder/cnnrelations/__init__.py +7 -0
  7. _balder/cnnrelations/and_connection_relation.py +176 -0
  8. _balder/cnnrelations/base_connection_relation.py +270 -0
  9. _balder/cnnrelations/or_connection_relation.py +65 -0
  10. _balder/collector.py +874 -0
  11. _balder/connection.py +863 -0
  12. _balder/connection_metadata.py +255 -0
  13. _balder/console/__init__.py +0 -0
  14. _balder/console/balder.py +58 -0
  15. _balder/controllers/__init__.py +12 -0
  16. _balder/controllers/base_device_controller.py +72 -0
  17. _balder/controllers/controller.py +29 -0
  18. _balder/controllers/device_controller.py +446 -0
  19. _balder/controllers/feature_controller.py +715 -0
  20. _balder/controllers/normal_scenario_setup_controller.py +402 -0
  21. _balder/controllers/scenario_controller.py +524 -0
  22. _balder/controllers/setup_controller.py +134 -0
  23. _balder/controllers/vdevice_controller.py +95 -0
  24. _balder/decorator_connect.py +104 -0
  25. _balder/decorator_covered_by.py +74 -0
  26. _balder/decorator_fixture.py +29 -0
  27. _balder/decorator_for_vdevice.py +118 -0
  28. _balder/decorator_gateway.py +34 -0
  29. _balder/decorator_insert_into_tree.py +52 -0
  30. _balder/decorator_parametrize.py +31 -0
  31. _balder/decorator_parametrize_by_feature.py +36 -0
  32. _balder/device.py +18 -0
  33. _balder/exceptions.py +182 -0
  34. _balder/executor/__init__.py +0 -0
  35. _balder/executor/basic_executable_executor.py +133 -0
  36. _balder/executor/basic_executor.py +205 -0
  37. _balder/executor/executor_tree.py +217 -0
  38. _balder/executor/parametrized_testcase_executor.py +52 -0
  39. _balder/executor/scenario_executor.py +169 -0
  40. _balder/executor/setup_executor.py +163 -0
  41. _balder/executor/testcase_executor.py +203 -0
  42. _balder/executor/unresolved_parametrized_testcase_executor.py +184 -0
  43. _balder/executor/variation_executor.py +882 -0
  44. _balder/exit_code.py +19 -0
  45. _balder/feature.py +74 -0
  46. _balder/feature_replacement_mapping.py +107 -0
  47. _balder/feature_vdevice_mapping.py +88 -0
  48. _balder/fixture_definition_scope.py +19 -0
  49. _balder/fixture_execution_level.py +22 -0
  50. _balder/fixture_manager.py +483 -0
  51. _balder/fixture_metadata.py +26 -0
  52. _balder/node_gateway.py +103 -0
  53. _balder/objects/__init__.py +0 -0
  54. _balder/objects/connections/__init__.py +0 -0
  55. _balder/objects/connections/osi_1_physical.py +116 -0
  56. _balder/objects/connections/osi_2_datalink.py +35 -0
  57. _balder/objects/connections/osi_3_network.py +47 -0
  58. _balder/objects/connections/osi_4_transport.py +40 -0
  59. _balder/objects/connections/osi_5_session.py +13 -0
  60. _balder/objects/connections/osi_6_presentation.py +13 -0
  61. _balder/objects/connections/osi_7_application.py +83 -0
  62. _balder/objects/devices/__init__.py +0 -0
  63. _balder/objects/devices/this_device.py +12 -0
  64. _balder/parametrization.py +75 -0
  65. _balder/plugin_manager.py +138 -0
  66. _balder/previous_executor_mark.py +23 -0
  67. _balder/routing_path.py +335 -0
  68. _balder/scenario.py +20 -0
  69. _balder/setup.py +18 -0
  70. _balder/solver.py +246 -0
  71. _balder/testresult.py +163 -0
  72. _balder/unmapped_vdevice.py +13 -0
  73. _balder/utils/__init__.py +0 -0
  74. _balder/utils/functions.py +103 -0
  75. _balder/utils/inner_device_managing_metaclass.py +14 -0
  76. _balder/utils/mixin_can_be_covered_by_executor.py +24 -0
  77. _balder/utils/typings.py +4 -0
  78. _balder/vdevice.py +9 -0
  79. balder/__init__.py +56 -0
  80. balder/connections.py +43 -0
  81. balder/devices.py +9 -0
  82. balder/exceptions.py +44 -0
  83. balder/parametrization.py +8 -0
  84. baldertest-0.1.0.dist-info/METADATA +356 -0
  85. baldertest-0.1.0.dist-info/RECORD +89 -0
  86. baldertest-0.1.0.dist-info/WHEEL +5 -0
  87. baldertest-0.1.0.dist-info/entry_points.txt +2 -0
  88. baldertest-0.1.0.dist-info/licenses/LICENSE +21 -0
  89. baldertest-0.1.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,217 @@
1
+ from __future__ import annotations
2
+ from typing import Union, List, Type, TYPE_CHECKING
3
+
4
+ from dataclasses import fields
5
+ from _balder.executor.setup_executor import SetupExecutor
6
+ from _balder.executor.basic_executable_executor import BasicExecutableExecutor
7
+ from _balder.fixture_execution_level import FixtureExecutionLevel
8
+ from _balder.testresult import ResultState, BranchBodyResult, ResultSummary
9
+ from _balder.previous_executor_mark import PreviousExecutorMark
10
+
11
+ if TYPE_CHECKING:
12
+ from _balder.setup import Setup
13
+ from _balder.fixture_manager import FixtureManager
14
+ from _balder.executor.scenario_executor import ScenarioExecutor
15
+ from _balder.executor.variation_executor import VariationExecutor
16
+ from _balder.executor.testcase_executor import TestcaseExecutor
17
+
18
+
19
+ class ExecutorTree(BasicExecutableExecutor):
20
+ """
21
+ This class is the root object of the executor tree structure
22
+ """
23
+ fixture_execution_level = FixtureExecutionLevel.SESSION
24
+ LINE_LENGTH = 120
25
+
26
+ def __init__(self, fixture_manager: FixtureManager):
27
+ super().__init__()
28
+ self._setup_executors: List[SetupExecutor] = []
29
+ self._fixture_manager = fixture_manager
30
+
31
+ # contains the result object for the BODY part of this branch (will be overwritten in :class:`TestcaseExecutor`)
32
+ self.body_result = BranchBodyResult(self)
33
+
34
+ # ---------------------------------- STATIC METHODS ----------------------------------------------------------------
35
+
36
+ # ---------------------------------- CLASS METHODS ----------------------------------------------------------------
37
+
38
+ # ---------------------------------- PROPERTIES --------------------------------------------------------------------
39
+
40
+ @property
41
+ def all_child_executors(self) -> List[BasicExecutableExecutor] | None:
42
+ return self._setup_executors
43
+
44
+ @property
45
+ def base_instance(self) -> object:
46
+ """returns None because this element is a ExecutorTree"""
47
+ return None
48
+
49
+ @property
50
+ def fixture_manager(self) -> FixtureManager:
51
+ """returns the fixture manager of this tree"""
52
+ return self._fixture_manager
53
+
54
+ @property
55
+ def parent_executor(self) -> None:
56
+ return None
57
+
58
+ # ---------------------------------- PROTECTED METHODS -------------------------------------------------------------
59
+
60
+ def _prepare_execution(self, show_discarded):
61
+ if not show_discarded:
62
+ self.update_inner_feature_reference_in_all_setups()
63
+
64
+ def _body_execution(self, show_discarded):
65
+ for cur_setup_executor in self.get_setup_executors():
66
+ prev_mark = cur_setup_executor.prev_mark
67
+ if cur_setup_executor.has_runnable_tests(show_discarded) or cur_setup_executor.has_skipped_tests():
68
+ cur_setup_executor.execute(show_discarded=show_discarded)
69
+ elif prev_mark == PreviousExecutorMark.SKIP:
70
+ cur_setup_executor.set_result_for_whole_branch(ResultState.SKIP)
71
+ elif prev_mark == PreviousExecutorMark.COVERED_BY:
72
+ cur_setup_executor.set_result_for_whole_branch(ResultState.COVERED_BY)
73
+ else:
74
+ cur_setup_executor.set_result_for_whole_branch(ResultState.NOT_RUN)
75
+
76
+ def _cleanup_execution(self, show_discarded):
77
+ pass
78
+
79
+ # ---------------------------------- METHODS -----------------------------------------------------------------------
80
+
81
+ def get_setup_executors(self) -> List[SetupExecutor]:
82
+ """returns all setup executors of this tree"""
83
+ return self._setup_executors
84
+
85
+ def get_all_scenario_executors(self) -> List[ScenarioExecutor]:
86
+ """
87
+ returns a list with all scenario executors
88
+ """
89
+ all_scenario_executor = []
90
+ for cur_setup_executor in self.get_setup_executors():
91
+ all_scenario_executor += cur_setup_executor.get_scenario_executors()
92
+ return all_scenario_executor
93
+
94
+ def get_all_variation_executors(self, return_discarded=False) -> List[VariationExecutor]:
95
+ """
96
+ returns a list with all variation executors
97
+ """
98
+ all_variation_executor = []
99
+ for cur_scenario_executor in self.get_all_scenario_executors():
100
+ all_variation_executor += cur_scenario_executor.get_variation_executors(return_discarded=return_discarded)
101
+ return all_variation_executor
102
+
103
+ def get_all_testcase_executors(self) -> List[TestcaseExecutor]:
104
+ """
105
+ returns a list with all testcase executors
106
+ """
107
+ all_testcase_executor = []
108
+ for cur_scenario_executor in self.get_all_scenario_executors():
109
+ for cur_variation_executor in cur_scenario_executor.get_variation_executors():
110
+ all_testcase_executor += cur_variation_executor.get_testcase_executors()
111
+ return all_testcase_executor
112
+
113
+ def add_setup_executor(self, setup_executor: SetupExecutor):
114
+ """
115
+ This method adds a new :class:`SetupExecutor` to the child element's list of this tree object
116
+ """
117
+ if not isinstance(setup_executor, SetupExecutor):
118
+ raise TypeError("the given object `setup_executor` must be of type `SetupExecutor`")
119
+ if setup_executor in self._setup_executors:
120
+ raise ValueError("the given object `setup_executor` already exists in child list")
121
+ self._setup_executors.append(setup_executor)
122
+
123
+ def get_executor_for_setup(self, setup: Type[Setup]) -> Union[SetupExecutor, None]:
124
+ """
125
+ This method searches for a SetupExecutor in the internal list for the given :class:`Setup` type
126
+
127
+ :param setup: the setup class for which the executor is being searched for
128
+
129
+ :return: returns the associated SetupExecutor or None if there are no matches for the given type
130
+ """
131
+ for cur_setup_executor in self._setup_executors:
132
+ if cur_setup_executor.base_setup_class.__class__ == setup:
133
+ return cur_setup_executor
134
+ # can not find some
135
+ return None
136
+
137
+ def cleanup_empty_executor_branches(self, consider_discarded=False):
138
+ to_remove_executor = []
139
+ for cur_setup_executor in self.get_setup_executors():
140
+ cur_setup_executor.cleanup_empty_executor_branches(consider_discarded=consider_discarded)
141
+ if len(cur_setup_executor.get_scenario_executors()) == 0:
142
+ # remove this whole executor because it has no children anymore
143
+ to_remove_executor.append(cur_setup_executor)
144
+ for cur_setup_executor in to_remove_executor:
145
+ self._setup_executors.remove(cur_setup_executor)
146
+
147
+ def update_inner_feature_reference_in_all_setups(self):
148
+ """
149
+ This method iterates over all setups and triggers the method that updates the inner feature references.
150
+
151
+ This needs to be done on ExecutorTree level, because this reference should also be accessible within session
152
+ fixtures (in case they have setup scope).
153
+ """
154
+ for cur_setup_executor in self.get_setup_executors():
155
+ cur_setup_executor.update_inner_referenced_feature_instances()
156
+
157
+ def execute(self, show_discarded=False) -> None:
158
+ """
159
+ This method executes this branch of the tree
160
+ """
161
+ start_text = "START TESTSESSION"
162
+ end_text = "FINISH TESTSESSION"
163
+
164
+ def print_line(text):
165
+ full_text = int((self.LINE_LENGTH - (len(start_text) + 2)) / 2) * "=" + " " + text + " "
166
+ full_text += "=" * (self.LINE_LENGTH - len(full_text))
167
+ print(full_text)
168
+
169
+ print_line(start_text)
170
+ # check if there exists runnable elements
171
+ runnables = [cur_exec.has_runnable_tests(consider_discarded_too=show_discarded)
172
+ for cur_exec in self.get_setup_executors()]
173
+ one_or_more_runnable_setups = None if len(runnables) == 0 else max(runnables)
174
+ if one_or_more_runnable_setups:
175
+ super().execute(show_discarded=show_discarded)
176
+ else:
177
+ print("NO EXECUTABLE SETUPS/SCENARIOS FOUND")
178
+ print_line(end_text)
179
+ summary = self.testsummary()
180
+ is_first = True
181
+ for cur_field in fields(ResultSummary):
182
+ if is_first:
183
+ is_first = False
184
+ else:
185
+ print(" | ", end="")
186
+ print(f"TOTAL {cur_field.name.upper()}: {getattr(summary, cur_field.name)}", end="")
187
+ print("")
188
+
189
+ def print_tree(self, show_discarded=False) -> None:
190
+ """this method is an auxiliary method which outputs the entire tree"""
191
+ print("RESOLVING OVERVIEW", end="\n\n")
192
+ for cur_setup_executor in self.get_setup_executors():
193
+ for cur_scenario_executor in cur_setup_executor.get_scenario_executors():
194
+ for cur_variation_executor in cur_scenario_executor.get_variation_executors(
195
+ return_discarded=show_discarded):
196
+ applicable = cur_variation_executor.prev_mark != PreviousExecutorMark.DISCARDED
197
+ start_char = '+' if applicable else 'X'
198
+ print(start_char * self.LINE_LENGTH)
199
+ applicability_str = "[APPLICABLE]" if applicable else "[DISCARDED] "
200
+ print(f"{start_char} {applicability_str} Scenario "
201
+ f"`{cur_scenario_executor.base_scenario_class.__class__.__qualname__}` <-> "
202
+ f"Setup `{cur_setup_executor.base_setup_class.__class__.__qualname__}`")
203
+ mapping_printings = {}
204
+ for cur_scenario_device, cur_setup_device in cur_variation_executor.base_device_mapping.items():
205
+ mapping_printings[f" {cur_scenario_device.__qualname__}"] = str(cur_setup_device.__qualname__)
206
+ max_len = max(len(cur_elem) for cur_elem in mapping_printings.keys())
207
+ for cur_key, cur_val in mapping_printings.items():
208
+ print(("{} {:<" + str(max_len) + "} = {}").format(start_char, cur_key, cur_val))
209
+ for cur_testcase_excutor in cur_variation_executor.get_testcase_executors():
210
+ print(f"{start_char} -> Testcase<"
211
+ f"{cur_testcase_excutor.base_testcase_callable.__qualname__}>")
212
+ if cur_variation_executor.prev_mark == PreviousExecutorMark.DISCARDED:
213
+ print(f"{start_char}")
214
+ print(f"{start_char} DISCARDED BECAUSE "
215
+ f"`{cur_variation_executor.not_applicable_variation_exc.args[0]}`")
216
+ print(('+' if applicable else 'X') * self.LINE_LENGTH)
217
+ print('')
@@ -0,0 +1,52 @@
1
+ from __future__ import annotations
2
+ from typing import Any, TYPE_CHECKING
3
+
4
+ from collections import OrderedDict
5
+ from _balder.utils.functions import get_method_type
6
+ from .testcase_executor import TestcaseExecutor
7
+
8
+ if TYPE_CHECKING:
9
+ from _balder.executor.unresolved_parametrized_testcase_executor import UnresolvedParametrizedTestcaseExecutor
10
+ from _balder.executor.variation_executor import VariationExecutor
11
+
12
+
13
+ class ParametrizedTestcaseExecutor(TestcaseExecutor):
14
+ """
15
+ special testcase executor for parametrized testcases
16
+ """
17
+ def __init__(
18
+ self,
19
+ testcase: callable,
20
+ parent: VariationExecutor,
21
+ parametrization: OrderedDict[str, Any] = None,
22
+ unresolved_group_obj: UnresolvedParametrizedTestcaseExecutor = None
23
+ ) -> None:
24
+ super().__init__(testcase=testcase, parent=parent)
25
+ self._parametrization = parametrization
26
+ # holds a reference to the parent unresolved object (if it has dynamic parametrized components
27
+ self._unresolved_group_obj: UnresolvedParametrizedTestcaseExecutor | None = unresolved_group_obj
28
+
29
+ @property
30
+ def full_test_name_str(self) -> str:
31
+ """
32
+ returns the name of the test method, that should be used in output
33
+ """
34
+ # try to get string name for parametrization
35
+ if self._parametrization:
36
+ # todo what if it is not convertable to string?
37
+ parametrization_name = ';'.join([str(e) for e in self._parametrization.values()])
38
+ return f"{super().full_test_name_str}[{parametrization_name}]"
39
+ return super().full_test_name_str
40
+
41
+ def get_all_test_method_args(self):
42
+ func_type = get_method_type(self.base_testcase_obj.__class__, self.base_testcase_callable)
43
+ all_kwargs = self.fixture_manager.get_all_attribute_values(
44
+ self,
45
+ self.base_testcase_obj.__class__,
46
+ self.base_testcase_callable,
47
+ func_type,
48
+ ignore_attributes=self._parametrization.keys()
49
+ )
50
+ if self._parametrization:
51
+ all_kwargs.update(self._parametrization)
52
+ return all_kwargs
@@ -0,0 +1,169 @@
1
+ from __future__ import annotations
2
+ from typing import Type, Union, List, TYPE_CHECKING
3
+
4
+ from _balder.fixture_execution_level import FixtureExecutionLevel
5
+ from _balder.testresult import ResultState, BranchBodyResult
6
+ from _balder.executor.basic_executable_executor import BasicExecutableExecutor
7
+ from _balder.executor.variation_executor import VariationExecutor
8
+ from _balder.previous_executor_mark import PreviousExecutorMark
9
+ from _balder.controllers.scenario_controller import ScenarioController
10
+
11
+ if TYPE_CHECKING:
12
+ from _balder.scenario import Scenario
13
+ from _balder.executor.setup_executor import SetupExecutor
14
+ from _balder.fixture_manager import FixtureManager
15
+
16
+
17
+ class ScenarioExecutor(BasicExecutableExecutor):
18
+ """
19
+ A ScenarioExecutor can contain :meth:`VariationExecutor` as children.
20
+ """
21
+ fixture_execution_level = FixtureExecutionLevel.SCENARIO
22
+
23
+ def __init__(self, scenario: Type[Scenario], parent: SetupExecutor):
24
+ super().__init__()
25
+ self._variation_executors: List[VariationExecutor] = []
26
+ # check if instance already exists
27
+ if hasattr(scenario, "_instance") and scenario._instance is not None and \
28
+ isinstance(scenario._instance, scenario):
29
+ self._base_scenario_class = scenario._instance
30
+ else:
31
+ self._base_scenario_class = scenario()
32
+ scenario._instance = self._base_scenario_class
33
+ self._parent_executor = parent
34
+ self._fixture_manager = parent.fixture_manager
35
+
36
+ # contains the result object for the BODY part of this branch
37
+ self.body_result = BranchBodyResult(self)
38
+
39
+ # if the related scenario class of this executor is decorated with ``@covered_by``, this property contains a
40
+ # list with the :class:`ScenarioExecutor` or/and :class:`TestcaseExecutor` that covers this one
41
+ self.covered_by_executors = None
42
+
43
+ # ---------------------------------- STATIC METHODS ----------------------------------------------------------------
44
+
45
+ # ---------------------------------- CLASS METHODS ----------------------------------------------------------------
46
+
47
+ # ---------------------------------- PROPERTIES --------------------------------------------------------------------
48
+
49
+ @property
50
+ def all_child_executors(self) -> List[VariationExecutor]:
51
+ return self._variation_executors
52
+
53
+ @property
54
+ def parent_executor(self) -> SetupExecutor:
55
+ return self._parent_executor
56
+
57
+ @property
58
+ def base_instance(self) -> object:
59
+ """
60
+ returns the base class instance to which this executor instance belongs
61
+ """
62
+ return self.base_scenario_class
63
+
64
+ @property
65
+ def base_scenario_class(self) -> Scenario:
66
+ """returns the :class:`Scenario` class that belongs to this executor"""
67
+ return self._base_scenario_class
68
+
69
+ @property
70
+ def base_scenario_controller(self) -> ScenarioController:
71
+ """returns the :class:`ScenarioController` for the setup object of this executor"""
72
+ return ScenarioController.get_for(self.base_scenario_class.__class__)
73
+
74
+ @property
75
+ def fixture_manager(self) -> FixtureManager:
76
+ """returns the current active fixture manager that belongs to this scenario executor"""
77
+ return self._fixture_manager
78
+
79
+ @property
80
+ def all_run_tests(self) -> List[callable]:
81
+ """returns a list of all test methods that are declared to `RUN` in their base :class:`Scenario` class"""
82
+ return self.base_scenario_controller.get_run_test_methods()
83
+
84
+ @property
85
+ def all_skip_tests(self) -> List[callable]:
86
+ """returns a list of all test methods that are declared to `SKIP` in their base :class:`Scenario` class"""
87
+ return self.base_scenario_controller.get_skip_test_methods()
88
+
89
+ @property
90
+ def all_ignore_tests(self) -> List[callable]:
91
+ """returns a list of all test methods that are declared to `IGNORE` in their base :class:`Scenario` class"""
92
+ return self.base_scenario_controller.get_ignore_test_methods()
93
+
94
+ # ---------------------------------- PROTECTED METHODS -------------------------------------------------------------
95
+
96
+ def _prepare_execution(self, show_discarded):
97
+ print(f" SCENARIO {self.base_scenario_class.__class__.__name__}")
98
+
99
+ def _body_execution(self, show_discarded):
100
+ for cur_variation_executor in self.get_variation_executors(return_discarded=show_discarded):
101
+ prev_mark = cur_variation_executor.prev_mark
102
+ if cur_variation_executor.has_runnable_tests() or cur_variation_executor.has_skipped_tests():
103
+ cur_variation_executor.execute(show_discarded=show_discarded)
104
+ elif prev_mark == PreviousExecutorMark.SKIP:
105
+ cur_variation_executor.set_result_for_whole_branch(ResultState.SKIP)
106
+ elif prev_mark == PreviousExecutorMark.COVERED_BY:
107
+ cur_variation_executor.set_result_for_whole_branch(ResultState.COVERED_BY)
108
+ else:
109
+ cur_variation_executor.set_result_for_whole_branch(ResultState.NOT_RUN)
110
+
111
+ def _cleanup_execution(self, show_discarded):
112
+ pass
113
+
114
+ # ---------------------------------- METHODS -----------------------------------------------------------------------
115
+
116
+ def get_variation_executors(self, return_discarded=False) -> List[VariationExecutor]:
117
+ """
118
+ :param return_discarded: True if the method should return discarded variations too
119
+
120
+ :return: returns all variation executors that are child executor of this scenario executor
121
+ """
122
+ if not return_discarded:
123
+ return [cur_executor for cur_executor in self._variation_executors
124
+ if cur_executor.prev_mark != PreviousExecutorMark.DISCARDED]
125
+ return self._variation_executors
126
+
127
+ def cleanup_empty_executor_branches(self, consider_discarded=False):
128
+ """
129
+ This method removes all sub executors that are empty and not relevant anymore.
130
+ """
131
+ to_remove_executor = []
132
+ for cur_variation_executor in self.get_variation_executors(return_discarded=consider_discarded):
133
+ if len(cur_variation_executor.get_testcase_executors()) == 0:
134
+ # remove this whole executor because it has no children anymore
135
+ to_remove_executor.append(cur_variation_executor)
136
+ for cur_variation_executor in to_remove_executor:
137
+ self._variation_executors.remove(cur_variation_executor)
138
+
139
+ def get_covered_by_element(self) -> List[Union[Scenario, callable]]:
140
+ """
141
+ This method returns a list of elements where the whole scenario is covered from. This means, that the whole
142
+ test methods in this scenario are already be covered from one of the elements in the list.
143
+ """
144
+ return self.base_scenario_controller.get_abs_covered_by_dict().get(None, [])
145
+
146
+ def add_variation_executor(self, variation_executor: VariationExecutor):
147
+ """
148
+ This method adds a new VariationExecutor to the child element list of the tree
149
+ """
150
+ if not isinstance(variation_executor, VariationExecutor):
151
+ raise TypeError("the given object `variation_executor` must be of type `VariationExecutor`")
152
+ if variation_executor in self._variation_executors:
153
+ raise ValueError("the given object `variation_executor` already exists in child list")
154
+ self._variation_executors.append(variation_executor)
155
+
156
+ def get_executor_for_device_mapping(self, device_mapping: dict) -> Union[VariationExecutor, None]:
157
+ """
158
+ This method searches for a VariationExecutor in the internal list for which the given device mapping is
159
+ contained in
160
+
161
+ :param device_mapping: the device_mapping dictionary for which the executor should be searched for
162
+
163
+ :return: returns the associated VariationExecutor or None if no matching could be found
164
+ """
165
+ for cur_variation_executor in self._variation_executors:
166
+ if cur_variation_executor.base_device_mapping == device_mapping:
167
+ return cur_variation_executor
168
+ # can not find some
169
+ return None
@@ -0,0 +1,163 @@
1
+ from __future__ import annotations
2
+ from typing import Type, Union, List, TYPE_CHECKING
3
+
4
+ from _balder.fixture_execution_level import FixtureExecutionLevel
5
+ from _balder.testresult import ResultState, BranchBodyResult
6
+ from _balder.executor.basic_executable_executor import BasicExecutableExecutor
7
+ from _balder.executor.scenario_executor import ScenarioExecutor
8
+ from _balder.previous_executor_mark import PreviousExecutorMark
9
+ from _balder.controllers.setup_controller import SetupController
10
+ from _balder.controllers.device_controller import DeviceController
11
+ from _balder.controllers.feature_controller import FeatureController
12
+
13
+ if TYPE_CHECKING:
14
+ from _balder.setup import Setup
15
+ from _balder.scenario import Scenario
16
+ from _balder.fixture_manager import FixtureManager
17
+ from _balder.executor.executor_tree import ExecutorTree
18
+
19
+
20
+ class SetupExecutor(BasicExecutableExecutor):
21
+ """
22
+ A SetupExecutor is the highest branch object of an :class:`ExecutorTree`. It contains all scenarios and the
23
+ underlying device mappings and their test cases that exist with this setup.
24
+ """
25
+ fixture_execution_level = FixtureExecutionLevel.SETUP
26
+
27
+ def __init__(self, setup: Type[Setup], parent: ExecutorTree):
28
+ super().__init__()
29
+ self._scenario_executors: List[ScenarioExecutor] = []
30
+ # check if instance already exists
31
+ if hasattr(setup, "_instance") and setup._instance is not None and isinstance(setup._instance, setup):
32
+ self._base_setup_class = setup._instance
33
+ else:
34
+ self._base_setup_class = setup()
35
+ setup._instance = self._base_setup_class
36
+ self._parent_executor = parent
37
+ self._fixture_manager = parent.fixture_manager
38
+
39
+ # contains the result object for the BODY part of this branch
40
+ self.body_result = BranchBodyResult(self)
41
+
42
+ # ---------------------------------- STATIC METHODS ----------------------------------------------------------------
43
+
44
+ # ---------------------------------- CLASS METHODS ----------------------------------------------------------------
45
+
46
+ # ---------------------------------- PROPERTIES --------------------------------------------------------------------
47
+
48
+ @property
49
+ def all_child_executors(self) -> List[ScenarioExecutor]:
50
+ return self._scenario_executors
51
+
52
+ @property
53
+ def parent_executor(self) -> ExecutorTree:
54
+ return self._parent_executor
55
+
56
+ @property
57
+ def base_instance(self) -> object:
58
+ """returns the base class instance to which this executor instance belongs"""
59
+ return self.base_setup_class
60
+
61
+ @property
62
+ def base_setup_class(self) -> Setup:
63
+ """returns the base :class:`Setup` that belongs to this executor"""
64
+ return self._base_setup_class
65
+
66
+ @property
67
+ def base_setup_controller(self) -> SetupController:
68
+ """returns the :class:`SetupController` for the setup object of this executor"""
69
+ return SetupController.get_for(self.base_setup_class.__class__)
70
+
71
+ @property
72
+ def fixture_manager(self) -> FixtureManager:
73
+ """returns the current active fixture manager for this executor"""
74
+ return self._fixture_manager
75
+
76
+ # ---------------------------------- PROTECTED METHODS -------------------------------------------------------------
77
+
78
+ def _prepare_execution(self, show_discarded):
79
+ print(f"SETUP {self.base_setup_class.__class__.__name__}")
80
+
81
+ def _body_execution(self, show_discarded):
82
+ for cur_scenario_executor in self.get_scenario_executors():
83
+ prev_mark = cur_scenario_executor.prev_mark
84
+ if cur_scenario_executor.has_runnable_tests(show_discarded) or cur_scenario_executor.has_skipped_tests():
85
+ cur_scenario_executor.execute(show_discarded=show_discarded)
86
+ elif prev_mark == PreviousExecutorMark.SKIP:
87
+ cur_scenario_executor.set_result_for_whole_branch(ResultState.SKIP)
88
+ elif prev_mark == PreviousExecutorMark.COVERED_BY:
89
+ cur_scenario_executor.set_result_for_whole_branch(ResultState.COVERED_BY)
90
+ else:
91
+ cur_scenario_executor.set_result_for_whole_branch(ResultState.NOT_RUN)
92
+
93
+ def _cleanup_execution(self, show_discarded):
94
+ pass
95
+
96
+ # ---------------------------------- METHODS -----------------------------------------------------------------------
97
+
98
+ def update_inner_referenced_feature_instances(self):
99
+ """
100
+ This method ensures that all inner referenced feature instances of all devices that are used in this setup are
101
+ set to the correct feature instance. For every existing device, this method goes trough all assigned features
102
+ and checks for a inner-referenced feature. It makes sure, that every inner-referenced feature variable has the
103
+ final assigned setup feature that belongs to it.
104
+
105
+ # TODO check where we validate that inner references feature exists in setup
106
+ """
107
+ for cur_setup_device in self.base_setup_controller.get_all_abs_inner_device_classes():
108
+ # these features are subclasses of the real defined one (because they are already the replaced ones)
109
+ all_device_features = DeviceController.get_for(cur_setup_device).get_all_instantiated_feature_objects()
110
+ all_instantiated_feature_objs_of_this_dev = [cur_feature for _, cur_feature in all_device_features.items()]
111
+ for _, cur_feature in all_device_features.items():
112
+ cur_feature_controller = FeatureController.get_for(cur_feature.__class__)
113
+ # now check the inner referenced features of this feature and check if that exists in the device
114
+ for cur_ref_feature_name, cur_ref_feature in \
115
+ cur_feature_controller.get_inner_referenced_features().items():
116
+ potential_candidates = [
117
+ candidate_feature for candidate_feature in all_instantiated_feature_objs_of_this_dev
118
+ if isinstance(candidate_feature, cur_ref_feature.__class__)]
119
+ if len(potential_candidates) != 1:
120
+ raise RuntimeError("found none or more than one potential replacing candidates")
121
+ replacing_candidate = potential_candidates[0]
122
+ # because `cur_feature` is only the object instance, the value will be overwritten only for this
123
+ # object
124
+ setattr(cur_feature, cur_ref_feature_name, replacing_candidate)
125
+
126
+ def get_scenario_executors(self) -> List[ScenarioExecutor]:
127
+ """returns a list with all scenario executors that belongs to this setup executor"""
128
+ return self._scenario_executors
129
+
130
+ def cleanup_empty_executor_branches(self, consider_discarded=False):
131
+ to_remove_executor = []
132
+ for cur_scenario_executor in self.get_scenario_executors():
133
+ cur_scenario_executor.cleanup_empty_executor_branches(consider_discarded=consider_discarded)
134
+ if len(cur_scenario_executor.get_variation_executors(return_discarded=consider_discarded)) == 0:
135
+ # remove this whole executor because it has no children anymore
136
+ to_remove_executor.append(cur_scenario_executor)
137
+ for cur_scenario_executor in to_remove_executor:
138
+ self._scenario_executors.remove(cur_scenario_executor)
139
+
140
+ def add_scenario_executor(self, scenario_executor: ScenarioExecutor):
141
+ """
142
+ This method adds a new ScenarioExecutor to the child element list of the tree
143
+ """
144
+ if not isinstance(scenario_executor, ScenarioExecutor):
145
+ raise TypeError("the given object `scenario_executor` must be of type type `ScenarioExecutor`")
146
+ if scenario_executor in self._scenario_executors:
147
+ raise ValueError("the given object `scenario_executor` already exists in child list")
148
+ self._scenario_executors.append(scenario_executor)
149
+
150
+ def get_executor_for_scenario(self, scenario: Type[Scenario]) -> Union[ScenarioExecutor, None]:
151
+ """
152
+ This method searches for a ScenarioExecutor in the internal list for which the given scenario is
153
+ contained in
154
+
155
+ :param scenario: the scenario class for which the executor should be searched for
156
+
157
+ :return: returns the associated ScenarioExecutor or None if no matching could be found
158
+ """
159
+ for cur_scenario_executor in self._scenario_executors:
160
+ if cur_scenario_executor.base_scenario_class.__class__ == scenario:
161
+ return cur_scenario_executor
162
+ # can not find some
163
+ return None