baldertest 0.1.0b11__py3-none-any.whl → 0.1.0b13__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 (41) hide show
  1. _balder/_version.py +8 -3
  2. _balder/cnnrelations/and_connection_relation.py +28 -1
  3. _balder/cnnrelations/base_connection_relation.py +1 -1
  4. _balder/collector.py +101 -113
  5. _balder/connection.py +76 -80
  6. _balder/controllers/device_controller.py +17 -29
  7. _balder/controllers/feature_controller.py +2 -2
  8. _balder/controllers/normal_scenario_setup_controller.py +1 -2
  9. _balder/controllers/scenario_controller.py +121 -0
  10. _balder/controllers/vdevice_controller.py +2 -23
  11. _balder/decorator_connect.py +3 -3
  12. _balder/decorator_covered_by.py +28 -36
  13. _balder/executor/basic_executable_executor.py +13 -6
  14. _balder/executor/basic_executor.py +31 -4
  15. _balder/executor/executor_tree.py +5 -4
  16. _balder/executor/parametrized_testcase_executor.py +2 -2
  17. _balder/executor/scenario_executor.py +12 -71
  18. _balder/executor/setup_executor.py +4 -3
  19. _balder/executor/testcase_executor.py +35 -19
  20. _balder/executor/unresolved_parametrized_testcase_executor.py +32 -57
  21. _balder/executor/variation_executor.py +127 -148
  22. _balder/feature.py +2 -1
  23. _balder/feature_replacement_mapping.py +107 -0
  24. _balder/feature_vdevice_mapping.py +88 -0
  25. _balder/fixture_manager.py +1 -1
  26. _balder/fixture_metadata.py +1 -1
  27. _balder/routing_path.py +27 -16
  28. _balder/scenario.py +2 -2
  29. _balder/setup.py +2 -1
  30. _balder/testresult.py +4 -3
  31. _balder/utils/__init__.py +0 -0
  32. _balder/{utils.py → utils/functions.py} +29 -31
  33. _balder/utils/inner_device_managing_metaclass.py +14 -0
  34. _balder/utils/mixin_can_be_covered_by_executor.py +24 -0
  35. _balder/utils/typings.py +4 -0
  36. {baldertest-0.1.0b11.dist-info → baldertest-0.1.0b13.dist-info}/METADATA +3 -2
  37. {baldertest-0.1.0b11.dist-info → baldertest-0.1.0b13.dist-info}/RECORD +41 -35
  38. {baldertest-0.1.0b11.dist-info → baldertest-0.1.0b13.dist-info}/WHEEL +1 -1
  39. {baldertest-0.1.0b11.dist-info → baldertest-0.1.0b13.dist-info}/entry_points.txt +0 -0
  40. {baldertest-0.1.0b11.dist-info → baldertest-0.1.0b13.dist-info/licenses}/LICENSE +0 -0
  41. {baldertest-0.1.0b11.dist-info → baldertest-0.1.0b13.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,8 @@
1
1
  from __future__ import annotations
2
- from typing import Type, Union, List, Dict, TYPE_CHECKING
2
+ from typing import Type, Union, List, TYPE_CHECKING
3
3
 
4
4
  from _balder.fixture_execution_level import FixtureExecutionLevel
5
5
  from _balder.testresult import ResultState, BranchBodyResult
6
- from _balder.utils import get_class_that_defines_method
7
6
  from _balder.executor.basic_executable_executor import BasicExecutableExecutor
8
7
  from _balder.executor.variation_executor import VariationExecutor
9
8
  from _balder.previous_executor_mark import PreviousExecutorMark
@@ -78,19 +77,19 @@ class ScenarioExecutor(BasicExecutableExecutor):
78
77
  return self._fixture_manager
79
78
 
80
79
  @property
81
- def all_run_tests(self):
80
+ def all_run_tests(self) -> List[callable]:
82
81
  """returns a list of all test methods that are declared to `RUN` in their base :class:`Scenario` class"""
83
- return self._base_scenario_class.RUN
82
+ return self.base_scenario_controller.get_run_test_methods()
84
83
 
85
84
  @property
86
- def all_skip_tests(self):
85
+ def all_skip_tests(self) -> List[callable]:
87
86
  """returns a list of all test methods that are declared to `SKIP` in their base :class:`Scenario` class"""
88
- return self._base_scenario_class.SKIP
87
+ return self.base_scenario_controller.get_skip_test_methods()
89
88
 
90
89
  @property
91
- def all_ignore_tests(self):
90
+ def all_ignore_tests(self) -> List[callable]:
92
91
  """returns a list of all test methods that are declared to `IGNORE` in their base :class:`Scenario` class"""
93
- return self._base_scenario_class.IGNORE
92
+ return self.base_scenario_controller.get_ignore_test_methods()
94
93
 
95
94
  # ---------------------------------- PROTECTED METHODS -------------------------------------------------------------
96
95
 
@@ -99,11 +98,12 @@ class ScenarioExecutor(BasicExecutableExecutor):
99
98
 
100
99
  def _body_execution(self, show_discarded):
101
100
  for cur_variation_executor in self.get_variation_executors(return_discarded=show_discarded):
102
- if cur_variation_executor.has_runnable_tests(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
103
  cur_variation_executor.execute(show_discarded=show_discarded)
104
- elif cur_variation_executor.prev_mark == PreviousExecutorMark.SKIP:
104
+ elif prev_mark == PreviousExecutorMark.SKIP:
105
105
  cur_variation_executor.set_result_for_whole_branch(ResultState.SKIP)
106
- elif cur_variation_executor.prev_mark == PreviousExecutorMark.COVERED_BY:
106
+ elif prev_mark == PreviousExecutorMark.COVERED_BY:
107
107
  cur_variation_executor.set_result_for_whole_branch(ResultState.COVERED_BY)
108
108
  else:
109
109
  cur_variation_executor.set_result_for_whole_branch(ResultState.NOT_RUN)
@@ -136,71 +136,12 @@ class ScenarioExecutor(BasicExecutableExecutor):
136
136
  for cur_variation_executor in to_remove_executor:
137
137
  self._variation_executors.remove(cur_variation_executor)
138
138
 
139
- def get_covered_by_dict(self) -> Dict[Union[Type[Scenario], callable], List[Union[Type[Scenario], callable]]]:
140
- """
141
- This method returns the complete resolved ``@covered_by`` dictionary for this scenario. It automatically
142
- cleans up every inheritance of the covered_by decorators for every parent class of our scenario.
143
- """
144
- def determine_most_inherited_class(class_list):
145
- for cur_candidate in class_list:
146
- candidate_is_valid = True
147
- for cur_other_candidate in class_list:
148
- if cur_candidate == cur_other_candidate:
149
- pass
150
- if not issubclass(cur_candidate, cur_other_candidate):
151
- candidate_is_valid = False
152
- break
153
- if candidate_is_valid:
154
- return cur_candidate
155
- return None
156
-
157
- # all data will be inherited while ``@covered_by`` overwrites elements only if there is a new decorator at the
158
- # overwritten method
159
- # -> we have to filter the dictionary and only return the value given for highest overwritten method
160
- relative_covered_by_dict = {}
161
- if hasattr(self.base_scenario_class, '_covered_by'):
162
- function_name_mapping = {}
163
- classes = []
164
- for cur_key in self.base_scenario_class._covered_by.keys():
165
- if issubclass(cur_key, Scenario):
166
- # this is a covered_by definition for the whole class
167
- classes.append(cur_key)
168
- else:
169
- # this is a covered_by definition for one test method
170
- if cur_key.__name__ in function_name_mapping.keys():
171
- function_name_mapping[cur_key.__name__] = [cur_key]
172
- else:
173
- function_name_mapping[cur_key.__name__].append(cur_key)
174
-
175
- # determine the highest definition for class statement (only if necessary)
176
- if len(classes) > 0:
177
- most_inherited_class = determine_most_inherited_class(classes)
178
- # this is the most inherited child -> add this definition
179
- relative_covered_by_dict[most_inherited_class] = \
180
- self.base_scenario_class._covered_by[most_inherited_class]
181
-
182
- # determine the highest definition for every test method
183
- for cur_function_name, cur_possible_candidates in function_name_mapping.items():
184
- classes = [get_class_that_defines_method(meth) for meth in cur_possible_candidates]
185
- most_inherited_class = determine_most_inherited_class(classes)
186
- most_inherited_test_method = cur_possible_candidates[classes.index(most_inherited_class)]
187
- # this is the most inherited test method -> add the definition of this one and replace the method with
188
- # this Scenario's one
189
- relative_covered_by_dict[getattr(self.base_scenario_class, cur_function_name)] = \
190
- self.base_scenario_class._covered_by[most_inherited_test_method]
191
- else:
192
- pass
193
- return relative_covered_by_dict
194
-
195
139
  def get_covered_by_element(self) -> List[Union[Scenario, callable]]:
196
140
  """
197
141
  This method returns a list of elements where the whole scenario is covered from. This means, that the whole
198
142
  test methods in this scenario are already be covered from one of the elements in the list.
199
143
  """
200
- covered_by_dict_resolved = self.get_covered_by_dict()
201
- if self in covered_by_dict_resolved.keys():
202
- return covered_by_dict_resolved[self]
203
- return []
144
+ return self.base_scenario_controller.get_abs_covered_by_dict().get(None, [])
204
145
 
205
146
  def add_variation_executor(self, variation_executor: VariationExecutor):
206
147
  """
@@ -80,11 +80,12 @@ class SetupExecutor(BasicExecutableExecutor):
80
80
 
81
81
  def _body_execution(self, show_discarded):
82
82
  for cur_scenario_executor in self.get_scenario_executors():
83
- if cur_scenario_executor.has_runnable_tests(consider_discarded_too=show_discarded):
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():
84
85
  cur_scenario_executor.execute(show_discarded=show_discarded)
85
- elif cur_scenario_executor.prev_mark == PreviousExecutorMark.SKIP:
86
+ elif prev_mark == PreviousExecutorMark.SKIP:
86
87
  cur_scenario_executor.set_result_for_whole_branch(ResultState.SKIP)
87
- elif cur_scenario_executor.prev_mark == PreviousExecutorMark.COVERED_BY:
88
+ elif prev_mark == PreviousExecutorMark.COVERED_BY:
88
89
  cur_scenario_executor.set_result_for_whole_branch(ResultState.COVERED_BY)
89
90
  else:
90
91
  cur_scenario_executor.set_result_for_whole_branch(ResultState.NOT_RUN)
@@ -9,15 +9,17 @@ from _balder.executor.basic_executable_executor import BasicExecutableExecutor
9
9
  from _balder.fixture_execution_level import FixtureExecutionLevel
10
10
  from _balder.previous_executor_mark import PreviousExecutorMark
11
11
  from _balder.testresult import ResultState, TestcaseResult
12
- from _balder.utils import inspect_method
12
+ from _balder.utils.functions import get_method_type
13
+ from _balder.utils.mixin_can_be_covered_by_executor import MixinCanBeCoveredByExecutor
13
14
 
14
15
  if TYPE_CHECKING:
16
+ from _balder.executor.scenario_executor import ScenarioExecutor
15
17
  from _balder.executor.variation_executor import VariationExecutor
16
18
  from _balder.fixture_manager import FixtureManager
17
19
  from _balder.scenario import Scenario
18
20
 
19
21
 
20
- class TestcaseExecutor(BasicExecutableExecutor):
22
+ class TestcaseExecutor(BasicExecutableExecutor, MixinCanBeCoveredByExecutor):
21
23
  """
22
24
  A TestcaseExecutor class represents an actual single test that can be executed. It therefore references exactly to a
23
25
  test method of a scenario that can be executed on the specific setup this executor belongs to.
@@ -65,6 +67,10 @@ class TestcaseExecutor(BasicExecutableExecutor):
65
67
  def parent_executor(self) -> VariationExecutor:
66
68
  return self._parent_executor
67
69
 
70
+ @property
71
+ def scenario_executor(self) -> ScenarioExecutor:
72
+ return self.parent_executor.parent_executor
73
+
68
74
  @property
69
75
  def base_instance(self) -> object:
70
76
  """
@@ -99,15 +105,23 @@ class TestcaseExecutor(BasicExecutableExecutor):
99
105
 
100
106
  def _prepare_execution(self, show_discarded):
101
107
  print(f" TEST {self.full_test_name_str} ", end='')
108
+
109
+ def _body_execution(self, show_discarded):
110
+ self.test_execution_time_sec = 0
111
+
102
112
  if self.should_be_skipped():
103
113
  self.body_result.set_result(ResultState.SKIP)
104
- self.execution_time_sec = 0
105
- print("[S]")
114
+ return
115
+ if self.should_be_ignored():
116
+ self.body_result.set_result(ResultState.NOT_RUN)
117
+ return
118
+ if self.is_covered_by():
119
+ self.body_result.set_result(ResultState.COVERED_BY)
120
+ return
106
121
 
107
- def _body_execution(self, show_discarded):
108
122
  start_time = time.perf_counter()
109
123
  try:
110
- _, func_type = inspect_method(self.base_testcase_callable)
124
+ func_type = get_method_type(self.base_testcase_obj.__class__, self.base_testcase_callable)
111
125
  all_args = self.get_all_test_method_args()
112
126
  if func_type == "staticmethod":
113
127
  # testcase is a staticmethod - no special first attribute
@@ -151,6 +165,16 @@ class TestcaseExecutor(BasicExecutableExecutor):
151
165
  return True
152
166
  return False
153
167
 
168
+ def is_covered_by(self):
169
+ """returns true if the testcase is covered-by"""
170
+ return self.prev_mark == PreviousExecutorMark.COVERED_BY
171
+
172
+ def has_skipped_tests(self) -> bool:
173
+ return self.prev_mark == PreviousExecutorMark.SKIP
174
+
175
+ def has_covered_by_tests(self) -> bool:
176
+ return self.prev_mark == PreviousExecutorMark.COVERED_BY
177
+
154
178
  def cleanup_empty_executor_branches(self, consider_discarded=False):
155
179
  """
156
180
  This method searches the whole tree and removes branches where an executor item has no own children. It can
@@ -160,25 +184,17 @@ class TestcaseExecutor(BasicExecutableExecutor):
160
184
  """
161
185
 
162
186
  def get_covered_by_element(self) -> List[Union[Scenario, callable]]:
163
- """
164
- This method returns a list of elements where the whole scenario is covered from. This means, that the whole
165
- test methods in this scenario are already be covered from every single element in the list.
166
- """
167
- all_covered_by_data = []
168
- scenario_executor = self.parent_executor.parent_executor
169
- scenario_class = scenario_executor.base_scenario_class
170
- covered_by_dict_resolved = scenario_executor.get_covered_by_dict()
171
- if self.base_testcase_callable in covered_by_dict_resolved.keys():
172
- all_covered_by_data += covered_by_dict_resolved[self.base_testcase_callable]
173
- if scenario_class in covered_by_dict_resolved.keys():
174
- all_covered_by_data += covered_by_dict_resolved[scenario_class]
187
+ covered_by_dict = self.scenario_executor.base_scenario_controller.get_abs_covered_by_dict()
188
+ all_covered_by_data = covered_by_dict.get(self.base_testcase_callable.__name__, [])
189
+ # also add all scenario specified covered-by elements
190
+ all_covered_by_data.extend(covered_by_dict.get(None, []))
175
191
  return all_covered_by_data
176
192
 
177
193
  def get_all_test_method_args(self):
178
194
  """
179
195
  returns all kwargs values for the test method
180
196
  """
181
- _, func_type = inspect_method(self.base_testcase_callable)
197
+ func_type = get_method_type(self.base_testcase_obj.__class__, self.base_testcase_callable)
182
198
  return self.fixture_manager.get_all_attribute_values(
183
199
  self,
184
200
  self.base_testcase_obj.__class__,
@@ -11,14 +11,16 @@ from _balder.executor.parametrized_testcase_executor import ParametrizedTestcase
11
11
  from _balder.parametrization import Parameter
12
12
  from _balder.previous_executor_mark import PreviousExecutorMark
13
13
  from _balder.testresult import BranchBodyResult
14
+ from _balder.utils.mixin_can_be_covered_by_executor import MixinCanBeCoveredByExecutor
14
15
 
15
16
  if TYPE_CHECKING:
17
+ from _balder.executor.scenario_executor import ScenarioExecutor
16
18
  from _balder.executor.variation_executor import VariationExecutor
17
19
  from _balder.scenario import Scenario
18
20
  from _balder.setup import Setup
19
21
 
20
22
 
21
- class UnresolvedParametrizedTestcaseExecutor(BasicExecutor):
23
+ class UnresolvedParametrizedTestcaseExecutor(BasicExecutor, MixinCanBeCoveredByExecutor):
22
24
  """
23
25
  This executor class represents a group of dynamically parametrized tests.
24
26
  """
@@ -37,9 +39,6 @@ class UnresolvedParametrizedTestcaseExecutor(BasicExecutor):
37
39
  # holds the specific static parameters for this unresolved group
38
40
  self._static_parameters = static_parameters if static_parameters is not None else {}
39
41
 
40
- # holds the dynamically created testcase executors as soon as this executor is entered
41
- self._testcase_executors = None
42
-
43
42
  # contains the result object for the BODY part of this branch
44
43
  self.body_result = BranchBodyResult(self)
45
44
 
@@ -47,14 +46,18 @@ class UnresolvedParametrizedTestcaseExecutor(BasicExecutor):
47
46
  def parent_executor(self) -> VariationExecutor:
48
47
  return self._parent_executor
49
48
 
49
+ @property
50
+ def scenario_executor(self) -> ScenarioExecutor:
51
+ return self.parent_executor.parent_executor
52
+
50
53
  @property
51
54
  def base_testcase_callable(self) -> callable:
52
55
  """returns the testcase function"""
53
56
  return self._base_testcase_callable
54
57
 
55
58
  @property
56
- def all_child_executors(self) -> List[ParametrizedTestcaseExecutor]:
57
- return self.get_testcase_executors()
59
+ def all_child_executors(self) -> None:
60
+ return None
58
61
 
59
62
  @property
60
63
  def base_testcase_obj(self) -> Scenario:
@@ -69,29 +72,11 @@ class UnresolvedParametrizedTestcaseExecutor(BasicExecutor):
69
72
  returns the base class instance to which this executor instance belongs"""
70
73
  return self._base_testcase_callable
71
74
 
72
- @property
73
- def parametrization_has_been_resolved(self) -> bool:
74
- """returns true if the parametrization has been resolved"""
75
- return self._testcase_executors is not None
76
-
77
- def has_runnable_tests(self, consider_discarded_too=False) -> bool:
78
- """
79
- This method returns true if this executor element is runnable. The method returns true if this element has
80
- `prev_mark=RUNNABLE` and minimum one of its children has `prev_mark=RUNNABLE` too.
81
-
82
- :param consider_discarded_too: True if the method allows DISCARDED elements too
83
- """
84
- if self.parametrization_has_been_resolved:
85
- return super().has_runnable_tests(consider_discarded_too=consider_discarded_too)
75
+ def has_skipped_tests(self) -> bool:
76
+ return self.prev_mark == PreviousExecutorMark.SKIP
86
77
 
87
- allowed_prev_marks = [PreviousExecutorMark.RUNNABLE]
88
-
89
- if consider_discarded_too:
90
- allowed_prev_marks.append(PreviousExecutorMark.DISCARDED)
91
-
92
- if self.prev_mark not in allowed_prev_marks:
93
- return False
94
- return True
78
+ def has_covered_by_tests(self) -> bool:
79
+ return self.prev_mark == PreviousExecutorMark.COVERED_BY
95
80
 
96
81
  def get_all_base_instances_of_this_branch(
97
82
  self, with_type: Type[Setup] | Type[Scenario] | Type[types.FunctionType],
@@ -105,43 +90,33 @@ class UnresolvedParametrizedTestcaseExecutor(BasicExecutor):
105
90
  return []
106
91
 
107
92
  def get_covered_by_element(self) -> List[Scenario | callable]:
108
- """
109
- This method returns a list of elements where the whole scenario is covered from. This means, that the whole
110
- test methods in this scenario are already be covered from every single element in the list.
111
- """
112
- all_covered_by_data = []
113
- scenario_executor = self.parent_executor.parent_executor
114
- scenario_class = scenario_executor.base_scenario_class
115
- covered_by_dict_resolved = scenario_executor.get_covered_by_dict()
116
- if self.base_testcase_callable in covered_by_dict_resolved.keys():
117
- all_covered_by_data += covered_by_dict_resolved[self.base_testcase_callable]
118
- if scenario_class in covered_by_dict_resolved.keys():
119
- all_covered_by_data += covered_by_dict_resolved[scenario_class]
93
+ covered_by_dict = self.scenario_executor.base_scenario_controller.get_abs_covered_by_dict()
94
+ all_covered_by_data = covered_by_dict.get(self.base_testcase_callable.__name__, [])
95
+ # also add all scenario specified covered-by elements
96
+ all_covered_by_data.extend(covered_by_dict.get(None, []))
120
97
  return all_covered_by_data
121
98
 
122
- def get_testcase_executors(self) -> List[ParametrizedTestcaseExecutor | UnresolvedParametrizedTestcaseExecutor]:
123
- """returns all sub testcase executors that belongs to this variation-executor"""
124
- if self._testcase_executors is None:
125
- return [self]
126
- return self._testcase_executors
127
-
128
- def resolve_dynamic_parametrization(self):
99
+ def get_resolved_parametrized_testcase_executors(self):
129
100
  """
130
101
  resolves the dynamic parametrization - should be called when setup features are active in the scenario
131
102
  """
132
- self._testcase_executors = []
103
+ executors = []
133
104
 
134
105
  parametrization = self.get_parametrization()
135
- if parametrization:
136
- for cur_parametrization in parametrization:
137
- self._testcase_executors.append(
138
- ParametrizedTestcaseExecutor(
139
- self._base_testcase_callable,
140
- parent=self.parent_executor,
141
- parametrization=cur_parametrization,
142
- unresolved_group_obj=self
143
- )
106
+
107
+ if not parametrization:
108
+ return []
109
+
110
+ for cur_parametrization in parametrization:
111
+ executors.append(
112
+ ParametrizedTestcaseExecutor(
113
+ self._base_testcase_callable,
114
+ parent=self.parent_executor,
115
+ parametrization=cur_parametrization,
116
+ unresolved_group_obj=self
144
117
  )
118
+ )
119
+ return executors
145
120
 
146
121
  def get_parametrization(self) -> List[OrderedDict[str, Any]] | None:
147
122
  """